--- /dev/null
+//===--- HIP.cpp - HIP Tool and ToolChain Implementations -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "HIP.h"
+#include "AMDGPU.h"
+#include "CommonArgs.h"
+#include "clang/Basic/Cuda.h"
+#include "clang/Basic/TargetID.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/InputInfo.h"
+#include "clang/Driver/Options.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetParser.h"
+
+using namespace clang::driver;
+using namespace clang::driver::toolchains;
+using namespace clang::driver::tools;
+using namespace clang;
+using namespace llvm::opt;
+
+#if defined(_WIN32) || defined(_WIN64)
+#define NULL_FILE "nul"
+#else
+#define NULL_FILE "/dev/null"
+#endif
+
+namespace {
+const unsigned HIPCodeObjectAlign = 4096;
+} // namespace
+
+void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA,
+ const InputInfoList &Inputs,
+ const InputInfo &Output,
+ const llvm::opt::ArgList &Args) const {
+ // Construct lld command.
+ // The output from ld.lld is an HSA code object file.
+ ArgStringList LldArgs{"-flavor", "gnu", "--no-undefined", "-shared",
+ "-plugin-opt=-amdgpu-internalize-symbols"};
+
+ auto &TC = getToolChain();
+ auto &D = TC.getDriver();
+ assert(!Inputs.empty() && "Must have at least one input.");
+ bool IsThinLTO = D.getLTOMode(/*IsOffload=*/true) == LTOK_Thin;
+ addLTOOptions(TC, Args, LldArgs, Output, Inputs[0], IsThinLTO);
+
+ // Extract all the -m options
+ std::vector<llvm::StringRef> Features;
+ amdgpu::getAMDGPUTargetFeatures(D, TC.getTriple(), Args, Features);
+
+ // Add features to mattr such as cumode
+ std::string MAttrString = "-plugin-opt=-mattr=";
+ for (auto OneFeature : unifyTargetFeatures(Features)) {
+ MAttrString.append(Args.MakeArgString(OneFeature));
+ if (OneFeature != Features.back())
+ MAttrString.append(",");
+ }
+ if (!Features.empty())
+ LldArgs.push_back(Args.MakeArgString(MAttrString));
+
+ // ToDo: Remove this option after AMDGPU backend supports ISA-level linking.
+ // Since AMDGPU backend currently does not support ISA-level linking, all
+ // called functions need to be imported.
+ if (IsThinLTO)
+ LldArgs.push_back(Args.MakeArgString("-plugin-opt=-force-import-all"));
+
+ for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
+ LldArgs.push_back(
+ Args.MakeArgString(Twine("-plugin-opt=") + A->getValue(0)));
+ }
+
+ if (C.getDriver().isSaveTempsEnabled())
+ LldArgs.push_back("-save-temps");
+
+ addLinkerCompressDebugSectionsOption(TC, Args, LldArgs);
+
+ LldArgs.append({"-o", Output.getFilename()});
+ for (auto Input : Inputs)
+ LldArgs.push_back(Input.getFilename());
+
+ if (Args.hasFlag(options::OPT_fgpu_sanitize, options::OPT_fno_gpu_sanitize,
+ false))
+ llvm::for_each(TC.getHIPDeviceLibs(Args), [&](auto BCFile) {
+ LldArgs.push_back(Args.MakeArgString(BCFile.Path));
+ });
+
+ const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld"));
+ C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+ Lld, LldArgs, Inputs, Output));
+}
+
+// Construct a clang-offload-bundler command to bundle code objects for
+// different GPU's into a HIP fat binary.
+void AMDGCN::constructHIPFatbinCommand(Compilation &C, const JobAction &JA,
+ StringRef OutputFileName, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &Args, const Tool& T) {
+ // Construct clang-offload-bundler command to bundle object files for
+ // for different GPU archs.
+ ArgStringList BundlerArgs;
+ BundlerArgs.push_back(Args.MakeArgString("-type=o"));
+ BundlerArgs.push_back(
+ Args.MakeArgString("-bundle-align=" + Twine(HIPCodeObjectAlign)));
+
+ // ToDo: Remove the dummy host binary entry which is required by
+ // clang-offload-bundler.
+ std::string BundlerTargetArg = "-targets=host-x86_64-unknown-linux";
+ std::string BundlerInputArg = "-inputs=" NULL_FILE;
+
+ // For code object version 2 and 3, the offload kind in bundle ID is 'hip'
+ // for backward compatibility. For code object version 4 and greater, the
+ // offload kind in bundle ID is 'hipv4'.
+ std::string OffloadKind = "hip";
+ if (getAMDGPUCodeObjectVersion(C.getDriver(), Args) >= 4)
+ OffloadKind = OffloadKind + "v4";
+ for (const auto &II : Inputs) {
+ const auto* A = II.getAction();
+ BundlerTargetArg = BundlerTargetArg + "," + OffloadKind +
+ "-amdgcn-amd-amdhsa--" +
+ StringRef(A->getOffloadingArch()).str();
+ BundlerInputArg = BundlerInputArg + "," + II.getFilename();
+ }
+ BundlerArgs.push_back(Args.MakeArgString(BundlerTargetArg));
+ BundlerArgs.push_back(Args.MakeArgString(BundlerInputArg));
+
+ std::string Output = std::string(OutputFileName);
+ auto BundlerOutputArg =
+ Args.MakeArgString(std::string("-outputs=").append(Output));
+ BundlerArgs.push_back(BundlerOutputArg);
+
+ const char *Bundler = Args.MakeArgString(
+ T.getToolChain().GetProgramPath("clang-offload-bundler"));
+ C.addCommand(std::make_unique<Command>(
+ JA, T, ResponseFileSupport::None(), Bundler, BundlerArgs, Inputs,
+ InputInfo(&JA, Args.MakeArgString(Output))));
+}
+
+/// Add Generated HIP Object File which has device images embedded into the
+/// host to the argument list for linking. Using MC directives, embed the
+/// device code and also define symbols required by the code generation so that
+/// the image can be retrieved at runtime.
+void AMDGCN::Linker::constructGenerateObjFileFromHIPFatBinary(
+ Compilation &C, const InputInfo &Output,
+ const InputInfoList &Inputs, const ArgList &Args,
+ const JobAction &JA) const {
+ const ToolChain &TC = getToolChain();
+ std::string Name =
+ std::string(llvm::sys::path::stem(Output.getFilename()));
+
+ // Create Temp Object File Generator,
+ // Offload Bundled file and Bundled Object file.
+ // Keep them if save-temps is enabled.
+ const char *McinFile;
+ const char *BundleFile;
+ if (C.getDriver().isSaveTempsEnabled()) {
+ McinFile = C.getArgs().MakeArgString(Name + ".mcin");
+ BundleFile = C.getArgs().MakeArgString(Name + ".hipfb");
+ } else {
+ auto TmpNameMcin = C.getDriver().GetTemporaryPath(Name, "mcin");
+ McinFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameMcin));
+ auto TmpNameFb = C.getDriver().GetTemporaryPath(Name, "hipfb");
+ BundleFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameFb));
+ }
+ constructHIPFatbinCommand(C, JA, BundleFile, Inputs, Args, *this);
+
+ // Create a buffer to write the contents of the temp obj generator.
+ std::string ObjBuffer;
+ llvm::raw_string_ostream ObjStream(ObjBuffer);
+
+ // Add MC directives to embed target binaries. We ensure that each
+ // section and image is 16-byte aligned. This is not mandatory, but
+ // increases the likelihood of data to be aligned with a cache block
+ // in several main host machines.
+ ObjStream << "# HIP Object Generator\n";
+ ObjStream << "# *** Automatically generated by Clang ***\n";
+ ObjStream << " .protected __hip_fatbin\n";
+ ObjStream << " .type __hip_fatbin,@object\n";
+ ObjStream << " .section .hip_fatbin,\"a\",@progbits\n";
+ ObjStream << " .globl __hip_fatbin\n";
+ ObjStream << " .p2align " << llvm::Log2(llvm::Align(HIPCodeObjectAlign))
+ << "\n";
+ ObjStream << "__hip_fatbin:\n";
+ ObjStream << " .incbin \"" << BundleFile << "\"\n";
+ ObjStream.flush();
+
+ // Dump the contents of the temp object file gen if the user requested that.
+ // We support this option to enable testing of behavior with -###.
+ if (C.getArgs().hasArg(options::OPT_fhip_dump_offload_linker_script))
+ llvm::errs() << ObjBuffer;
+
+ // Open script file and write the contents.
+ std::error_code EC;
+ llvm::raw_fd_ostream Objf(McinFile, EC, llvm::sys::fs::OF_None);
+
+ if (EC) {
+ C.getDriver().Diag(clang::diag::err_unable_to_make_temp) << EC.message();
+ return;
+ }
+
+ Objf << ObjBuffer;
+
+ ArgStringList McArgs{"-o", Output.getFilename(),
+ McinFile, "--filetype=obj"};
+ const char *Mc = Args.MakeArgString(TC.GetProgramPath("llvm-mc"));
+ C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+ Mc, McArgs, Inputs, Output));
+}
+
+// For amdgcn the inputs of the linker job are device bitcode and output is
+// object file. It calls llvm-link, opt, llc, then lld steps.
+void AMDGCN::Linker::ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const ArgList &Args,
+ const char *LinkingOutput) const {
+ if (Inputs.size() > 0 &&
+ Inputs[0].getType() == types::TY_Image &&
+ JA.getType() == types::TY_Object)
+ return constructGenerateObjFileFromHIPFatBinary(C, Output, Inputs, Args, JA);
+
+ if (JA.getType() == types::TY_HIP_FATBIN)
+ return constructHIPFatbinCommand(C, JA, Output.getFilename(), Inputs, Args, *this);
+
+ return constructLldCommand(C, JA, Inputs, Output, Args);
+}
+
+HIPToolChain::HIPToolChain(const Driver &D, const llvm::Triple &Triple,
+ const ToolChain &HostTC, const ArgList &Args)
+ : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {
+ // Lookup binaries into the driver directory, this is used to
+ // discover the clang-offload-bundler executable.
+ getProgramPaths().push_back(getDriver().Dir);
+}
+
+void HIPToolChain::addClangTargetOptions(
+ const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args,
+ Action::OffloadKind DeviceOffloadingKind) const {
+ HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
+
+ assert(DeviceOffloadingKind == Action::OFK_HIP &&
+ "Only HIP offloading kinds are supported for GPUs.");
+
+ CC1Args.push_back("-fcuda-is-device");
+
+ if (DriverArgs.hasFlag(options::OPT_fcuda_approx_transcendentals,
+ options::OPT_fno_cuda_approx_transcendentals, false))
+ CC1Args.push_back("-fcuda-approx-transcendentals");
+
+ if (!DriverArgs.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc,
+ false))
+ CC1Args.append({"-mllvm", "-amdgpu-internalize-symbols"});
+
+ StringRef MaxThreadsPerBlock =
+ DriverArgs.getLastArgValue(options::OPT_gpu_max_threads_per_block_EQ);
+ if (!MaxThreadsPerBlock.empty()) {
+ std::string ArgStr =
+ std::string("--gpu-max-threads-per-block=") + MaxThreadsPerBlock.str();
+ CC1Args.push_back(DriverArgs.MakeArgStringRef(ArgStr));
+ }
+
+ CC1Args.push_back("-fcuda-allow-variadic-functions");
+
+ // Default to "hidden" visibility, as object level linking will not be
+ // supported for the foreseeable future.
+ if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+ options::OPT_fvisibility_ms_compat)) {
+ CC1Args.append({"-fvisibility", "hidden"});
+ CC1Args.push_back("-fapply-global-visibility-to-externs");
+ }
+
+ llvm::for_each(getHIPDeviceLibs(DriverArgs), [&](auto BCFile) {
+ CC1Args.push_back(BCFile.ShouldInternalize ? "-mlink-builtin-bitcode"
+ : "-mlink-bitcode-file");
+ CC1Args.push_back(DriverArgs.MakeArgString(BCFile.Path));
+ });
+}
+
+llvm::opt::DerivedArgList *
+HIPToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
+ StringRef BoundArch,
+ Action::OffloadKind DeviceOffloadKind) const {
+ DerivedArgList *DAL =
+ HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);
+ if (!DAL)
+ DAL = new DerivedArgList(Args.getBaseArgs());
+
+ const OptTable &Opts = getDriver().getOpts();
+
+ for (Arg *A : Args) {
+ if (!shouldSkipArgument(A))
+ DAL->append(A);
+ }
+
+ if (!BoundArch.empty()) {
+ DAL->eraseArg(options::OPT_mcpu_EQ);
+ DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_mcpu_EQ), BoundArch);
+ checkTargetID(*DAL);
+ }
+
+ return DAL;
+}
+
+Tool *HIPToolChain::buildLinker() const {
+ assert(getTriple().getArch() == llvm::Triple::amdgcn);
+ return new tools::AMDGCN::Linker(*this);
+}
+
+void HIPToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
+ HostTC.addClangWarningOptions(CC1Args);
+}
+
+ToolChain::CXXStdlibType
+HIPToolChain::GetCXXStdlibType(const ArgList &Args) const {
+ return HostTC.GetCXXStdlibType(Args);
+}
+
+void HIPToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const {
+ HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
+}
+
+void HIPToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args,
+ ArgStringList &CC1Args) const {
+ HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
+}
+
+void HIPToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
+ ArgStringList &CC1Args) const {
+ HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
+}
+
+void HIPToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs,
+ ArgStringList &CC1Args) const {
+ RocmInstallation.AddHIPIncludeArgs(DriverArgs, CC1Args);
+}
+
+SanitizerMask HIPToolChain::getSupportedSanitizers() const {
+ // The HIPToolChain only supports sanitizers in the sense that it allows
+ // sanitizer arguments on the command line if they are supported by the host
+ // toolchain. The HIPToolChain will actually ignore any command line
+ // arguments for any of these "supported" sanitizers. That means that no
+ // sanitization of device code is actually supported at this time.
+ //
+ // This behavior is necessary because the host and device toolchains
+ // invocations often share the command line, so the device toolchain must
+ // tolerate flags meant only for the host toolchain.
+ return HostTC.getSupportedSanitizers();
+}
+
+VersionTuple HIPToolChain::computeMSVCVersion(const Driver *D,
+ const ArgList &Args) const {
+ return HostTC.computeMSVCVersion(D, Args);
+}
+
+llvm::SmallVector<ToolChain::BitCodeLibraryInfo, 12>
+HIPToolChain::getHIPDeviceLibs(const llvm::opt::ArgList &DriverArgs) const {
+ llvm::SmallVector<BitCodeLibraryInfo, 12> BCLibs;
+ if (DriverArgs.hasArg(options::OPT_nogpulib))
+ return {};
+ ArgStringList LibraryPaths;
+
+ // Find in --hip-device-lib-path and HIP_LIBRARY_PATH.
+ for (auto Path : RocmInstallation.getRocmDeviceLibPathArg())
+ LibraryPaths.push_back(DriverArgs.MakeArgString(Path));
+
+ addDirectoryList(DriverArgs, LibraryPaths, "", "HIP_DEVICE_LIB_PATH");
+
+ // Maintain compatability with --hip-device-lib.
+ auto BCLibArgs = DriverArgs.getAllArgValues(options::OPT_hip_device_lib_EQ);
+ if (!BCLibArgs.empty()) {
+ llvm::for_each(BCLibArgs, [&](StringRef BCName) {
+ StringRef FullName;
+ for (std::string LibraryPath : LibraryPaths) {
+ SmallString<128> Path(LibraryPath);
+ llvm::sys::path::append(Path, BCName);
+ FullName = Path;
+ if (llvm::sys::fs::exists(FullName)) {
+ BCLibs.push_back(FullName);
+ return;
+ }
+ }
+ getDriver().Diag(diag::err_drv_no_such_file) << BCName;
+ });
+ } else {
+ if (!RocmInstallation.hasDeviceLibrary()) {
+ getDriver().Diag(diag::err_drv_no_rocm_device_lib) << 0;
+ return {};
+ }
+ StringRef GpuArch = getGPUArch(DriverArgs);
+ assert(!GpuArch.empty() && "Must have an explicit GPU arch.");
+
+ // If --hip-device-lib is not set, add the default bitcode libraries.
+ if (DriverArgs.hasFlag(options::OPT_fgpu_sanitize,
+ options::OPT_fno_gpu_sanitize, false)) {
+ auto AsanRTL = RocmInstallation.getAsanRTLPath();
+ if (AsanRTL.empty()) {
+ unsigned DiagID = getDriver().getDiags().getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "AMDGPU address sanitizer runtime library (asanrtl) is not found. "
+ "Please install ROCm device library which supports address "
+ "sanitizer");
+ getDriver().Diag(DiagID);
+ return {};
+ } else
+ BCLibs.push_back({AsanRTL.str(), /*ShouldInternalize=*/false});
+ }
+
+ // Add the HIP specific bitcode library.
+ BCLibs.push_back(RocmInstallation.getHIPPath());
+
+ // Add common device libraries like ocml etc.
+ for (auto N : getCommonDeviceLibNames(DriverArgs, GpuArch.str()))
+ BCLibs.push_back(StringRef(N));
+
+ // Add instrument lib.
+ auto InstLib =
+ DriverArgs.getLastArgValue(options::OPT_gpu_instrument_lib_EQ);
+ if (InstLib.empty())
+ return BCLibs;
+ if (llvm::sys::fs::exists(InstLib))
+ BCLibs.push_back(InstLib);
+ else
+ getDriver().Diag(diag::err_drv_no_such_file) << InstLib;
+ }
+
+ return BCLibs;
+}
+
+void HIPToolChain::checkTargetID(const llvm::opt::ArgList &DriverArgs) const {
+ auto PTID = getParsedTargetID(DriverArgs);
+ if (PTID.OptionalTargetID && !PTID.OptionalGPUArch) {
+ getDriver().Diag(clang::diag::err_drv_bad_target_id)
+ << PTID.OptionalTargetID.getValue();
+ return;
+ }
+
+ assert(PTID.OptionalFeatures && "Invalid return from getParsedTargetID");
+ auto &FeatureMap = PTID.OptionalFeatures.getValue();
+ // Sanitizer is not supported with xnack-.
+ if (DriverArgs.hasFlag(options::OPT_fgpu_sanitize,
+ options::OPT_fno_gpu_sanitize, false)) {
+ auto Loc = FeatureMap.find("xnack");
+ if (Loc != FeatureMap.end() && !Loc->second) {
+ auto &Diags = getDriver().getDiags();
+ auto DiagID = Diags.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "'-fgpu-sanitize' is not compatible with offload arch '%0'. "
+ "Use an offload arch without 'xnack-' instead");
+ Diags.Report(DiagID) << PTID.OptionalTargetID.getValue();
+ }
+ }
+}
--- /dev/null
+//===--- HIP.h - HIP ToolChain Implementations ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_HIP_H
+#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_HIP_H
+
+#include "clang/Driver/ToolChain.h"
+#include "clang/Driver/Tool.h"
+#include "AMDGPU.h"
+
+namespace clang {
+namespace driver {
+
+namespace tools {
+
+namespace AMDGCN {
+ // Construct command for creating HIP fatbin.
+ void constructHIPFatbinCommand(Compilation &C, const JobAction &JA,
+ StringRef OutputFileName, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &TCArgs, const Tool& T);
+
+// Runs llvm-link/opt/llc/lld, which links multiple LLVM bitcode, together with
+// device library, then compiles it to ISA in a shared object.
+class LLVM_LIBRARY_VISIBILITY Linker : public Tool {
+public:
+ Linker(const ToolChain &TC) : Tool("AMDGCN::Linker", "amdgcn-link", TC) {}
+
+ bool hasIntegratedCPP() const override { return false; }
+
+ void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &TCArgs,
+ const char *LinkingOutput) const override;
+
+private:
+
+ void constructLldCommand(Compilation &C, const JobAction &JA,
+ const InputInfoList &Inputs, const InputInfo &Output,
+ const llvm::opt::ArgList &Args) const;
+
+ // Construct command for creating Object from HIP fatbin.
+ void constructGenerateObjFileFromHIPFatBinary(Compilation &C,
+ const InputInfo &Output,
+ const InputInfoList &Inputs,
+ const llvm::opt::ArgList &Args,
+ const JobAction &JA) const;
+};
+
+} // end namespace AMDGCN
+} // end namespace tools
+
+namespace toolchains {
+
+class LLVM_LIBRARY_VISIBILITY HIPToolChain final : public ROCMToolChain {
+public:
+ HIPToolChain(const Driver &D, const llvm::Triple &Triple,
+ const ToolChain &HostTC, const llvm::opt::ArgList &Args);
+
+ const llvm::Triple *getAuxTriple() const override {
+ return &HostTC.getTriple();
+ }
+
+ llvm::opt::DerivedArgList *
+ TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
+ Action::OffloadKind DeviceOffloadKind) const override;
+ void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args,
+ Action::OffloadKind DeviceOffloadKind) const override;
+ void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const override;
+ CXXStdlibType GetCXXStdlibType(const llvm::opt::ArgList &Args) const override;
+ void
+ AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+ void AddClangCXXStdlibIncludeArgs(
+ const llvm::opt::ArgList &Args,
+ llvm::opt::ArgStringList &CC1Args) const override;
+ void AddIAMCUIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+ void AddHIPIncludeArgs(const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) const override;
+ llvm::SmallVector<BitCodeLibraryInfo, 12>
+ getHIPDeviceLibs(const llvm::opt::ArgList &Args) const override;
+
+ SanitizerMask getSupportedSanitizers() const override;
+
+ VersionTuple
+ computeMSVCVersion(const Driver *D,
+ const llvm::opt::ArgList &Args) const override;
+
+ unsigned GetDefaultDwarfVersion() const override { return 5; }
+
+ const ToolChain &HostTC;
+ void checkTargetID(const llvm::opt::ArgList &DriverArgs) const override;
+
+protected:
+ Tool *buildLinker() const override;
+};
+
+} // end namespace toolchains
+} // end namespace driver
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_HIP_H
--- /dev/null
+{
+ 'version': 0,
+ 'roots': [
+ { 'name': '@DIR@', 'type': 'directory',
+ 'contents': [
+ { 'name': 'only-virtual-file.blacklist', 'type': 'file',
+ 'external-contents': '@REAL_FILE@'
+ },
+ { 'name': 'invalid-virtual-file.blacklist', 'type': 'file',
+ 'external-contents': '@NONEXISTENT_FILE@'
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+// RUN: %clang_cc1 -fsanitize=alignment -fsanitize-recover=alignment -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_alignment_assumption" --check-prefixes=CHECK
+
+// CHECK-LABEL: @baseline
+void *baseline(void *x) {
+ // CHECK: call void @__ubsan_handle_alignment_assumption(
+ return __builtin_assume_aligned(x, 1);
+}
+
+// CHECK-LABEL: blacklist_0
+__attribute__((no_sanitize("undefined"))) void *blacklist_0(void *x) {
+ return __builtin_assume_aligned(x, 1);
+}
+
+// CHECK-LABEL: blacklist_1
+__attribute__((no_sanitize("alignment"))) void *blacklist_1(void *x) {
+ return __builtin_assume_aligned(x, 1);
+}
+
+// CHECK-LABEL: dont_ignore_volatile_ptrs
+void *dont_ignore_volatile_ptrs(void * volatile x) {
+ // CHECK: call void @__ubsan_handle_alignment_assumption(
+ return __builtin_assume_aligned(x, 1);
+}
+
+// CHECK-LABEL: ignore_volatiles
+void *ignore_volatiles(volatile void * x) {
+ return __builtin_assume_aligned(x, 1);
+}
--- /dev/null
+// RUN: %clang_cc1 -x c -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow"
+// RUN: %clang_cc1 -x c -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow"
+
+// RUN: %clang_cc1 -x c++ -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow"
+// RUN: %clang_cc1 -x c++ -fno-delete-null-pointer-checks -fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_pointer_overflow"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// CHECK-LABEL: @baseline
+char *baseline(char *base, unsigned long offset) {
+ // CHECK: call void @__ubsan_handle_pointer_overflow(
+ return base + offset;
+}
+
+// CHECK-LABEL: @blacklist_0
+__attribute__((no_sanitize("undefined"))) char *blacklist_0(char *base, unsigned long offset) {
+ return base + offset;
+}
+
+// CHECK-LABEL: @blacklist_1
+__attribute__((no_sanitize("pointer-overflow"))) char *blacklist_1(char *base, unsigned long offset) {
+ return base + offset;
+}
+
+// CHECK-LABEL: @ignore_non_default_address_space
+__attribute__((address_space(1))) char *ignore_non_default_address_space(__attribute__((address_space(1))) char *base, unsigned long offset) {
+ return base + offset;
+}
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+// Verify ubsan doesn't emit checks for blacklisted functions and files
+// RUN: echo "fun:hash" > %t-func.blacklist
+// RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t-file.blacklist
+// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT
+// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=%t-func.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FUNC
+// RUN: %clang_cc1 -fsanitize=unsigned-integer-overflow -fsanitize-blacklist=%t-file.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=FILE
+
+unsigned i;
+
+// DEFAULT: @hash
+// FUNC: @hash
+// FILE: @hash
+unsigned hash() {
+// DEFAULT: call {{.*}}void @__ubsan
+// FUNC-NOT: call {{.*}}void @__ubsan
+// FILE-NOT: call {{.*}}void @__ubsan
+ return i * 37;
+}
+
+// DEFAULT: @add
+// FUNC: @add
+// FILE: @add
+unsigned add() {
+// DEFAULT: call {{.*}}void @__ubsan
+// FUNC: call {{.*}}void @__ubsan
+// FILE-NOT: call {{.*}}void @__ubsan
+ return i + 1;
+}
--- /dev/null
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOBL %s
+
+// Check that blacklisting cfi and cfi-vcall work correctly
+// RUN: echo "[cfi-vcall]" > %t.vcall.txt
+// RUN: echo "type:std::*" >> %t.vcall.txt
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.vcall.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s
+//
+// RUN: echo "[cfi]" > %t.cfi.txt
+// RUN: echo "type:std::*" >> %t.cfi.txt
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.cfi.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s
+
+// Check that blacklisting non-vcall modes does not affect vcalls
+// RUN: echo "[cfi-icall|cfi-nvcall|cfi-cast-strict|cfi-derived-cast|cfi-unrelated-cast]" > %t.other.txt
+// RUN: echo "type:std::*" >> %t.other.txt
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.other.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOBL %s
+
+struct S1 {
+ virtual void f();
+};
+
+namespace std {
+
+struct S2 {
+ virtual void f();
+};
+
+}
+
+// CHECK: define{{.*}}s1f
+// NOBL: llvm.type.test
+// NOSTD: llvm.type.test
+void s1f(S1 *s1) {
+ s1->f();
+}
+
+// CHECK: define{{.*}}s2f
+// NOBL: llvm.type.test
+// NOSTD-NOT: llvm.type.test
+void s2f(std::S2 *s2) {
+ s2->f();
+}
--- /dev/null
+// RUN: %clang -Xclang -fexperimental-debug-variable-locations -fsyntax-only -disable-llvm-passes %s
+int main() {}
--- /dev/null
+// RUN: %clang_cc1 -triple aarch64 -verify -fsyntax-only %s
+
+__attribute__((target("branch-protection=foo"))) // expected-error {{invalid or misplaced branch protection specification 'foo'}}
+void badvalue0() {}
+
+__attribute__((target("branch-protection=+bti"))) // expected-error {{invalid or misplaced branch protection specification '<empty>'}}
+void badvalue1() {}
+
+__attribute__((target("branch-protection=bti+"))) // expected-error {{invalid or misplaced branch protection specification '<empty>'}}
+void badvalue2() {}
+
+__attribute__((target("branch-protection=pac-ret+bkey"))) // expected-error {{invalid or misplaced branch protection specification 'bkey'}}
+void badvalue3() {}
+
+__attribute__((target("branch-protection=bti+leaf"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}}
+void badoption0() {}
+
+__attribute__((target("branch-protection=bti+leaf+pac-ret"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}}
+void badorder0() {}
+
+__attribute__ ((target("branch-protection=pac-ret+bti+leaf"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}}
+void badorder1() {}
--- /dev/null
+# TODO(phosek): We should use the runtimes build instead configured with
+# LLVM_ENABLE_RUNTIMES=libcxxabi;libcxx to avoid duplication of logic.
+
+cmake_minimum_required(VERSION 3.13.4)
+project(custom-libcxx C CXX)
+
+find_package(Python3 REQUIRED COMPONENTS Interpreter)
+
+# Build static libcxxabi.
+set(LIBCXXABI_ENABLE_SHARED OFF CACHE BOOL "")
+set(LIBCXXABI_ENABLE_EXCEPTIONS ON CACHE BOOL "")
+set(LIBCXXABI_HERMETIC_STATIC_LIBRARY ON CACHE STRING "")
+set(LIBCXXABI_LIBCXX_PATH ${COMPILER_RT_LIBCXX_PATH} CACHE PATH "")
+set(LIBCXXABI_INCLUDE_TESTS OFF CACHE BOOL "")
+add_subdirectory(${COMPILER_RT_LIBCXXABI_PATH} ${CMAKE_CURRENT_BINARY_DIR}/cxxabi)
+
+# Build static libcxx without exceptions.
+set(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY OFF CACHE BOOL "")
+set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
+set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
+set(LIBCXX_HERMETIC_STATIC_LIBRARY ON CACHE BOOL "")
+
+# Use above libcxxabi.
+set(LIBCXX_CXX_ABI "libcxxabi" CACHE STRING "")
+set(LIBCXX_CXX_ABI_INTREE 1)
+set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
+set(LIBCXX_CXX_ABI_INCLUDE_PATHS ${COMPILER_RT_LIBCXXABI_PATH}/include CACHE PATH "")
+
+add_subdirectory(${COMPILER_RT_LIBCXX_PATH} ${CMAKE_CURRENT_BINARY_DIR}/cxx)
--- /dev/null
+//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A fast memory allocator that does not support free() nor realloc().
+// All allocations are forever.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H
+#define SANITIZER_PERSISTENT_ALLOCATOR_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+template <typename T>
+class PersistentAllocator {
+ public:
+ T *alloc(uptr count = 1);
+ uptr allocated() const { return atomic_load_relaxed(&mapped_size); }
+
+ void TestOnlyUnmap();
+
+ private:
+ T *tryAlloc(uptr count);
+ T *refillAndAlloc(uptr count);
+ mutable StaticSpinMutex mtx; // Protects alloc of new blocks.
+ atomic_uintptr_t region_pos; // Region allocator for Node's.
+ atomic_uintptr_t region_end;
+ atomic_uintptr_t mapped_size;
+
+ struct BlockInfo {
+ const BlockInfo *next;
+ uptr ptr;
+ uptr size;
+ };
+ const BlockInfo *curr;
+};
+
+template <typename T>
+inline T *PersistentAllocator<T>::tryAlloc(uptr count) {
+ // Optimisic lock-free allocation, essentially try to bump the region ptr.
+ for (;;) {
+ uptr cmp = atomic_load(®ion_pos, memory_order_acquire);
+ uptr end = atomic_load(®ion_end, memory_order_acquire);
+ uptr size = count * sizeof(T);
+ if (cmp == 0 || cmp + size > end)
+ return nullptr;
+ if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size,
+ memory_order_acquire))
+ return reinterpret_cast<T *>(cmp);
+ }
+}
+
+template <typename T>
+inline T *PersistentAllocator<T>::alloc(uptr count) {
+ // First, try to allocate optimisitically.
+ T *s = tryAlloc(count);
+ if (LIKELY(s))
+ return s;
+ return refillAndAlloc(count);
+}
+
+template <typename T>
+inline T *PersistentAllocator<T>::refillAndAlloc(uptr count) {
+ // If failed, lock, retry and alloc new superblock.
+ SpinMutexLock l(&mtx);
+ for (;;) {
+ T *s = tryAlloc(count);
+ if (s)
+ return s;
+ atomic_store(®ion_pos, 0, memory_order_relaxed);
+ uptr size = count * sizeof(T) + sizeof(BlockInfo);
+ uptr allocsz = RoundUpTo(Max<uptr>(size, 64u * 1024u), GetPageSizeCached());
+ uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
+ BlockInfo *new_block = (BlockInfo *)(mem + allocsz) - 1;
+ new_block->next = curr;
+ new_block->ptr = mem;
+ new_block->size = allocsz;
+ curr = new_block;
+
+ atomic_fetch_add(&mapped_size, allocsz, memory_order_relaxed);
+
+ allocsz -= sizeof(BlockInfo);
+ atomic_store(®ion_end, mem + allocsz, memory_order_release);
+ atomic_store(®ion_pos, mem, memory_order_release);
+ }
+}
+
+template <typename T>
+void PersistentAllocator<T>::TestOnlyUnmap() {
+ while (curr) {
+ uptr mem = curr->ptr;
+ uptr allocsz = curr->size;
+ curr = curr->next;
+ UnmapOrDie((void *)mem, allocsz);
+ }
+ internal_memset(this, 0, sizeof(*this));
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
--- /dev/null
+//===-- tsan_update_shadow_word.inc -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Body of the hottest inner loop.
+// If we wrap this body into a function, compilers (both gcc and clang)
+// produce sligtly less efficient code.
+//===----------------------------------------------------------------------===//
+do {
+ const unsigned kAccessSize = 1 << kAccessSizeLog;
+ u64 *sp = &shadow_mem[idx];
+ old = LoadShadow(sp);
+ if (LIKELY(old.IsZero())) {
+ if (!stored) {
+ StoreIfNotYetStored(sp, &store_word);
+ stored = true;
+ }
+ break;
+ }
+ // is the memory access equal to the previous?
+ if (LIKELY(Shadow::Addr0AndSizeAreEqual(cur, old))) {
+ // same thread?
+ if (LIKELY(Shadow::TidsAreEqual(old, cur))) {
+ if (LIKELY(old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))) {
+ StoreIfNotYetStored(sp, &store_word);
+ stored = true;
+ }
+ break;
+ }
+ if (HappensBefore(old, thr)) {
+ if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) {
+ StoreIfNotYetStored(sp, &store_word);
+ stored = true;
+ }
+ break;
+ }
+ if (LIKELY(old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)))
+ break;
+ goto RACE;
+ }
+ // Do the memory access intersect?
+ if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) {
+ if (Shadow::TidsAreEqual(old, cur))
+ break;
+ if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
+ break;
+ if (LIKELY(HappensBefore(old, thr)))
+ break;
+ goto RACE;
+ }
+ // The accesses do not intersect.
+ break;
+} while (0);
--- /dev/null
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Tests whether scaling the Entropic scheduling weight based on input execution
+// time is effective or not. Inputs of size less than 7 will take at least 100
+// microseconds more than inputs of size greater than or equal to 7. Inputs of
+// size greater than 7 in the corpus should be favored by the exec-time-scaled
+// Entropic scheduling policy than the input of size less than 7 in the corpus,
+// eventually finding the crashing input with less executions.
+#include <chrono>
+#include <cstdint>
+#include <thread>
+
+static volatile int Sink;
+static volatile int *Nil = nullptr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 10)
+ return 0; // To make the test quicker.
+
+ if (Size < 7) {
+ // execute a lot slower than the crashing input below.
+ size_t ExecTimeUSec = 100;
+ std::this_thread::sleep_for(std::chrono::microseconds(ExecTimeUSec));
+ Sink = 7;
+
+ if (Size > 0 && Data[0] == 0xaa && Size > 1 && Data[1] == 0xbb &&
+ Size > 2 && Data[2] == 0xcc && Size > 3 && Data[3] == 0xdd &&
+ Size > 4 && Data[4] == 0xee && Size > 5 && Data[5] == 0xff)
+ Sink += 7;
+ }
+
+ // Give unique coverage for each input of size (7, 8, 9, 10)
+ if (Size == 7)
+ Sink = -7;
+
+ if (Size == 8)
+ Sink = -8;
+
+ if (Size == 9)
+ Sink = -9;
+
+ if (Size == 10)
+ Sink = -10;
+
+ if (Sink < 0 && Data[0] == 0xab && Data[1] == 0xcd)
+ *Nil = 42; // crash.
+
+ return 0;
+}
--- /dev/null
+REQUIRES: linux, x86_64
+RUN: %cpp_compiler %S/EntropicScalePerExecTimeTest.cpp -o %t-EntropicScalePerExecTimeTest
+RUN: not %run %t-EntropicScalePerExecTimeTest -entropic=1 -entropic_scale_per_exec_time=1 -seed=1 -runs=200000 -max_len=10
+
+# Without -entropic_scale_per_exec_time=1, the crash takes longer to find since
+# the slow path is explored first. This test is disabled because it sometimes
+# finds the bug under certain configs.
+DISABLED: %run %t-EntropicScalePerExecTimeTest -entropic=1 -seed=1 -runs=200000 -max_len=10
--- /dev/null
+// Check mem_info_cache_entries option.
+
+// RUN: %clangxx_memprof -O0 %s -o %t && %env_memprof_opts=log_path=stderr:mem_info_cache_entries=15:print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s
+
+// CHECK: Set 14 miss rate: 0 / {{.*}} = 0.00%
+// CHECK-NOT: Set
+
+int main() {
+ return 0;
+}
--- /dev/null
+// Check print_mem_info_cache_miss_rate and
+// print_mem_info_cache_miss_rate_details options.
+
+// RUN: %clangxx_memprof -O0 %s -o %t
+// RUN: %env_memprof_opts=log_path=stderr:print_mem_info_cache_miss_rate=1 %run %t 2>&1 | FileCheck %s
+// RUN: %env_memprof_opts=log_path=stderr:print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s --check-prefix=DETAILS
+
+// CHECK: Overall miss rate: 0 / {{.*}} = 0.00%
+// DETAILS: Set 0 miss rate: 0 / {{.*}} = 0.00%
+// DETAILS: Set 16380 miss rate: 0 / {{.*}} = 0.00%
+
+int main() {
+ return 0;
+}
--- /dev/null
+// RUN: %clang -fsanitize=alignment -fno-sanitize-recover=alignment -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption "
+
+// RUN: rm -f %tmp
+// RUN: echo "[alignment]" >> %tmp
+// RUN: echo "fun:main" >> %tmp
+// RUN: %clang -fsanitize=alignment -fno-sanitize-recover=alignment -fsanitize-ignorelist=%tmp -O0 %s -o %t && %run %t 2>&1
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ __builtin_assume_aligned(ptr + 1, 0x8000);
+ // CHECK: {{.*}}alignment-assumption-ignorelist.cpp:[[@LINE-1]]:32: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
--- /dev/null
+set(LLVM_BUILD_32_BITS ON CACHE BOOL "")
--- /dev/null
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_POINTER_SAFETY_H
+#define _LIBCPP___MEMORY_POINTER_SAFETY_H
+
+#include <__config>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_CXX03_LANG)
+
+enum class pointer_safety : unsigned char {
+ relaxed,
+ preferred,
+ strict
+};
+
+inline _LIBCPP_INLINE_VISIBILITY
+pointer_safety get_pointer_safety() _NOEXCEPT {
+ return pointer_safety::relaxed;
+}
+
+_LIBCPP_FUNC_VIS void declare_reachable(void* __p);
+_LIBCPP_FUNC_VIS void declare_no_pointers(char* __p, size_t __n);
+_LIBCPP_FUNC_VIS void undeclare_no_pointers(char* __p, size_t __n);
+_LIBCPP_FUNC_VIS void* __undeclare_reachable(void* __p);
+
+template <class _Tp>
+inline _LIBCPP_INLINE_VISIBILITY
+_Tp*
+undeclare_reachable(_Tp* __p)
+{
+ return static_cast<_Tp*>(__undeclare_reachable(__p));
+}
+
+#endif // !C++03
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_POINTER_SAFETY_H
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// Make sure that `std::atomic` doesn't work with `_ExtInt`. The intent is to
+// disable them for now until their behavior can be designed better later.
+// See https://reviews.llvm.org/D84049 for details.
+
+// UNSUPPORTED: apple-clang-12
+
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++03
+
+#include <atomic>
+
+int main(int, char**)
+{
+ // expected-error@atomic:*1 {{_Atomic cannot be applied to integer type '_ExtInt(32)'}}
+ std::atomic<_ExtInt(32)> x(42);
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// Test that including <atomic> fails to compile when _LIBCPP_HAS_NO_THREADS
+// is defined.
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_HAS_NO_THREADS
+
+#include <atomic>
+
+int main(int, char**)
+{
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// XFAIL: libcpp-has-no-threads
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+#error This should be XFAILed for the purpose of detecting that the LIT feature\
+ 'libcpp-has-no-threads' is available iff _LIBCPP_HAS_NO_THREADS is defined
+#endif
+
+int main(int, char**)
+{
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <algorithm>
+
+#include <algorithm>
+#include <functional>
+
+#include "test_macros.h"
+
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+template<class>
+struct Intable {
+ TEST_CONSTEXPR operator int() const { return 1; }
+};
+
+struct Tester {
+ using Element = Holder<Incomplete>*;
+ Element data[10];
+};
+
+TEST_CONSTEXPR_CXX20 bool test()
+{
+ Tester t {};
+ Tester u {};
+ Tester::Element value = nullptr;
+ Intable<Tester::Element> count;
+
+ // THESE RELY ON ADL SWAP IN PRACTICE:
+ // swap_ranges, iter_swap, reverse, rotate, partition
+ // sort, nth_element
+ // pop_heap, sort_heap, partial_sort, partial_sort_copy
+ // next_permutation, prev_permutation
+ // stable_partition, stable_sort, inplace_merge
+ // THESE RELY ON ADL SWAP IN THEORY:
+ // push_heap, make_heap
+
+ (void)std::all_of(t.data, t.data+10, [](void*){ return true; });
+ (void)std::any_of(t.data, t.data+10, [](void*){ return true; });
+ (void)std::copy(t.data, t.data+10, u.data);
+ (void)std::copy_n(t.data, count, u.data);
+ (void)std::copy_backward(t.data, t.data+10, u.data+10);
+ (void)std::count(t.data, t.data+10, value);
+ (void)std::count_if(t.data, t.data+10, [](void*){ return true; });
+ (void)std::distance(t.data, t.data+10);
+ (void)std::fill(t.data, t.data+10, value);
+ (void)std::fill_n(t.data, count, value);
+ (void)std::find_if(t.data, t.data+10, [](void*){ return true; });
+ (void)std::find_if_not(t.data, t.data+10, [](void*){ return true; });
+ (void)std::for_each(t.data, t.data+10, [](void*){});
+#if TEST_STD_VER >= 17
+ (void)std::for_each_n(t.data, count, [](void*){});
+#endif
+ (void)std::generate(t.data, t.data+10, [](){ return nullptr; });
+ (void)std::generate_n(t.data, count, [](){ return nullptr; });
+ (void)std::is_partitioned(t.data, t.data+10, [](void*){ return true; });
+ (void)std::move(t.data, t.data+10, u.data);
+ (void)std::move_backward(t.data, t.data+10, u.data+10);
+ (void)std::none_of(t.data, t.data+10, [](void*){ return true; });
+ (void)std::partition_copy(t.data, t.data+5, u.data, u.data+5, [](void*){ return true; });
+ (void)std::partition_point(t.data, t.data+10, [](void*){ return true; });
+ (void)std::remove(t.data, t.data+10, value);
+ (void)std::remove_copy(t.data, t.data+10, u.data, value);
+ (void)std::remove_copy_if(t.data, t.data+10, u.data, [](void*){ return true; });
+ (void)std::remove_if(t.data, t.data+10, [](void*){ return true; });
+ (void)std::replace(t.data, t.data+10, value, value);
+ (void)std::replace_copy(t.data, t.data+10, u.data, value, value);
+ (void)std::replace_copy_if(t.data, t.data+10, u.data, [](void*){ return true; }, value);
+ (void)std::replace_if(t.data, t.data+10, [](void*){ return true; }, value);
+ (void)std::reverse_copy(t.data, t.data+10, u.data);
+ (void)std::rotate_copy(t.data, t.data+5, t.data+10, u.data);
+ // TODO: shift_left
+ // TODO: shift_right
+ (void)std::transform(t.data, t.data+10, u.data, [](void*){ return nullptr; });
+
+ // WITHOUT COMPARATORS
+ (void)std::adjacent_find(t.data, t.data+10);
+ (void)std::binary_search(t.data, t.data+10, t.data[5]);
+ (void)std::equal(t.data, t.data+10, u.data);
+ (void)std::equal_range(t.data, t.data+10, t.data[5]);
+ (void)std::find_end(t.data, t.data+10, u.data, u.data+5);
+ (void)std::includes(t.data, t.data+10, u.data, u.data+10);
+ (void)std::is_heap(t.data, t.data+10);
+ (void)std::is_heap_until(t.data, t.data+10);
+ (void)std::is_permutation(t.data, t.data+10, u.data);
+ (void)std::is_sorted(t.data, t.data+10);
+ (void)std::is_sorted_until(t.data, t.data+10);
+ (void)std::lexicographical_compare(t.data, t.data+10, u.data, u.data+10);
+ // TODO: lexicographical_compare_three_way
+ (void)std::lower_bound(t.data, t.data+10, t.data[5]);
+ (void)std::max(value, value);
+ (void)std::max({ value, value });
+ (void)std::max_element(t.data, t.data+10);
+ (void)std::merge(t.data, t.data+5, t.data+5, t.data+10, u.data);
+ (void)std::min(value, value);
+ (void)std::min({ value, value });
+ (void)std::min_element(t.data, t.data+10);
+ (void)std::minmax(value, value);
+ (void)std::minmax({ value, value });
+ (void)std::minmax_element(t.data, t.data+10);
+ (void)std::mismatch(t.data, t.data+10, u.data);
+ (void)std::search(t.data, t.data+10, u.data, u.data+5);
+ (void)std::search_n(t.data, t.data+10, count, value);
+ (void)std::set_difference(t.data, t.data+5, t.data+5, t.data+10, u.data);
+ (void)std::set_intersection(t.data, t.data+5, t.data+5, t.data+10, u.data);
+ (void)std::set_symmetric_difference(t.data, t.data+5, t.data+5, t.data+10, u.data);
+ (void)std::set_union(t.data, t.data+5, t.data+5, t.data+10, u.data);
+ (void)std::unique(t.data, t.data+10);
+ (void)std::unique_copy(t.data, t.data+10, u.data);
+ (void)std::upper_bound(t.data, t.data+10, t.data[5]);
+#if TEST_STD_VER >= 14
+ (void)std::equal(t.data, t.data+10, u.data, u.data+10);
+ (void)std::is_permutation(t.data, t.data+10, u.data, u.data+10);
+ (void)std::mismatch(t.data, t.data+10, u.data, u.data+10);
+#endif
+#if TEST_STD_VER >= 20
+ (void)std::clamp(value, value, value);
+#endif
+
+ // WITH COMPARATORS
+ (void)std::adjacent_find(t.data, t.data+10, std::equal_to<void*>());
+ (void)std::binary_search(t.data, t.data+10, value, std::less<void*>());
+ (void)std::equal(t.data, t.data+10, u.data, std::equal_to<void*>());
+ (void)std::equal_range(t.data, t.data+10, value, std::less<void*>());
+ (void)std::find_end(t.data, t.data+10, u.data, u.data+5, std::equal_to<void*>());
+ (void)std::includes(t.data, t.data+10, u.data, u.data+10, std::less<void*>());
+ (void)std::is_heap(t.data, t.data+10, std::less<void*>());
+ (void)std::is_heap_until(t.data, t.data+10, std::less<void*>());
+ (void)std::is_permutation(t.data, t.data+10, u.data, std::equal_to<void*>());
+ (void)std::is_sorted(t.data, t.data+10, std::less<void*>());
+ (void)std::is_sorted_until(t.data, t.data+10, std::less<void*>());
+ (void)std::lexicographical_compare(t.data, t.data+10, u.data, u.data+10, std::less<void*>());
+ // TODO: lexicographical_compare_three_way
+ (void)std::lower_bound(t.data, t.data+10, value, std::less<void*>());
+ (void)std::max(value, value, std::less<void*>());
+ (void)std::max({ value, value }, std::less<void*>());
+ (void)std::max_element(t.data, t.data+10, std::less<void*>());
+ (void)std::merge(t.data, t.data+5, t.data+5, t.data+10, u.data, std::less<void*>());
+ (void)std::min(value, value, std::less<void*>());
+ (void)std::min({ value, value }, std::less<void*>());
+ (void)std::min_element(t.data, t.data+10, std::less<void*>());
+ (void)std::minmax(value, value, std::less<void*>());
+ (void)std::minmax({ value, value }, std::less<void*>());
+ (void)std::minmax_element(t.data, t.data+10, std::less<void*>());
+ (void)std::mismatch(t.data, t.data+10, u.data, std::equal_to<void*>());
+ (void)std::search(t.data, t.data+10, u.data, u.data+5, std::equal_to<void*>());
+ (void)std::search_n(t.data, t.data+10, count, value, std::equal_to<void*>());
+ (void)std::set_difference(t.data, t.data+5, t.data+5, t.data+10, u.data, std::less<void*>());
+ (void)std::set_intersection(t.data, t.data+5, t.data+5, t.data+10, u.data, std::less<void*>());
+ (void)std::set_symmetric_difference(t.data, t.data+5, t.data+5, t.data+10, u.data, std::less<void*>());
+ (void)std::set_union(t.data, t.data+5, t.data+5, t.data+10, u.data, std::less<void*>());
+ (void)std::unique(t.data, t.data+10, std::equal_to<void*>());
+ (void)std::unique_copy(t.data, t.data+10, u.data, std::equal_to<void*>());
+ (void)std::upper_bound(t.data, t.data+10, value, std::less<void*>());
+#if TEST_STD_VER >= 14
+ (void)std::equal(t.data, t.data+10, u.data, u.data+10, std::equal_to<void*>());
+ (void)std::is_permutation(t.data, t.data+10, u.data, u.data+10, std::equal_to<void*>());
+ (void)std::mismatch(t.data, t.data+10, u.data, u.data+10, std::equal_to<void*>());
+#endif
+#if TEST_STD_VER >= 20
+ (void)std::clamp(value, value, value, std::less<void*>());
+#endif
+
+ return true;
+}
+
+int main(int, char**)
+{
+ test();
+#if TEST_STD_VER >= 20
+ static_assert(test());
+#endif
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// template <class T>
+// struct atomic
+// {
+// bool is_lock_free() const volatile noexcept;
+// bool is_lock_free() const noexcept;
+// void store(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
+// void store(T desr, memory_order m = memory_order_seq_cst) noexcept;
+// T load(memory_order m = memory_order_seq_cst) const volatile noexcept;
+// T load(memory_order m = memory_order_seq_cst) const noexcept;
+// operator T() const volatile noexcept;
+// operator T() const noexcept;
+// T exchange(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
+// T exchange(T desr, memory_order m = memory_order_seq_cst) noexcept;
+// bool compare_exchange_weak(T& expc, T desr,
+// memory_order s, memory_order f) volatile noexcept;
+// bool compare_exchange_weak(T& expc, T desr, memory_order s, memory_order f) noexcept;
+// bool compare_exchange_strong(T& expc, T desr,
+// memory_order s, memory_order f) volatile noexcept;
+// bool compare_exchange_strong(T& expc, T desr,
+// memory_order s, memory_order f) noexcept;
+// bool compare_exchange_weak(T& expc, T desr,
+// memory_order m = memory_order_seq_cst) volatile noexcept;
+// bool compare_exchange_weak(T& expc, T desr,
+// memory_order m = memory_order_seq_cst) noexcept;
+// bool compare_exchange_strong(T& expc, T desr,
+// memory_order m = memory_order_seq_cst) volatile noexcept;
+// bool compare_exchange_strong(T& expc, T desr,
+// memory_order m = memory_order_seq_cst) noexcept;
+//
+// atomic() noexcept = default;
+// constexpr atomic(T desr) noexcept;
+// atomic(const atomic&) = delete;
+// atomic& operator=(const atomic&) = delete;
+// atomic& operator=(const atomic&) volatile = delete;
+// T operator=(T) volatile noexcept;
+// T operator=(T) noexcept;
+// };
+
+#include <atomic>
+#include <new>
+#include <cassert>
+#include <thread> // for thread_id
+#include <chrono> // for nanoseconds
+
+struct NotTriviallyCopyable {
+ NotTriviallyCopyable ( int i ) : i_(i) {}
+ NotTriviallyCopyable ( const NotTriviallyCopyable &rhs) : i_(rhs.i_) {}
+ int i_;
+};
+
+template <class T, class >
+void test ( T t ) {
+ std::atomic<T> t0(t);
+}
+
+int main(int, char**)
+{
+ test(NotTriviallyCopyable(42));
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// iterator insert(const_iterator position, const value_type& x);
+
+#include <vector>
+#include <cassert>
+#include <cstddef>
+
+#include "test_macros.h"
+#include "test_allocator.h"
+#include "min_allocator.h"
+#include "asan_testing.h"
+
+int main(int, char**)
+{
+ {
+ std::vector<int> v(100);
+ std::vector<int>::iterator i = v.insert(v.cbegin() + 10, 1);
+ assert(v.size() == 101);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ assert(v[j] == 1);
+ for (++j; j < 101; ++j)
+ assert(v[j] == 0);
+ }
+ {
+ std::vector<int> v(100);
+ while(v.size() < v.capacity()) v.push_back(0); // force reallocation
+ size_t sz = v.size();
+ std::vector<int>::iterator i = v.insert(v.cbegin() + 10, 1);
+ assert(v.size() == sz + 1);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ std::size_t j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ assert(v[j] == 1);
+ for (++j; j < v.size(); ++j)
+ assert(v[j] == 0);
+ }
+ {
+ std::vector<int> v(100);
+ while(v.size() < v.capacity()) v.push_back(0);
+ v.pop_back(); v.pop_back(); // force no reallocation
+ size_t sz = v.size();
+ std::vector<int>::iterator i = v.insert(v.cbegin() + 10, 1);
+ assert(v.size() == sz + 1);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ std::size_t j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ assert(v[j] == 1);
+ for (++j; j < v.size(); ++j)
+ assert(v[j] == 0);
+ }
+ {
+ std::vector<int, limited_allocator<int, 300> > v(100);
+ std::vector<int, limited_allocator<int, 300> >::iterator i = v.insert(v.cbegin() + 10, 1);
+ assert(v.size() == 101);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ assert(v[j] == 1);
+ for (++j; j < 101; ++j)
+ assert(v[j] == 0);
+ }
+#if TEST_STD_VER >= 11
+ {
+ std::vector<int, min_allocator<int>> v(100);
+ std::vector<int, min_allocator<int>>::iterator i = v.insert(v.cbegin() + 10, 1);
+ assert(v.size() == 101);
+ assert(is_contiguous_container_asan_correct(v));
+ assert(i == v.begin() + 10);
+ int j;
+ for (j = 0; j < 10; ++j)
+ assert(v[j] == 0);
+ assert(v[j] == 1);
+ for (++j; j < 101; ++j)
+ assert(v[j] == 0);
+ }
+#endif
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// <charconv> feature macros
+
+/* Constant Value
+ __cpp_lib_to_chars 201611L
+
+*/
+
+#include <charconv>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**)
+{
+// ensure that the macros that are supposed to be defined in <utility> are defined.
+
+/*
+#if !defined(__cpp_lib_fooby)
+# error "__cpp_lib_fooby is not defined"
+#elif __cpp_lib_fooby < 201606L
+# error "__cpp_lib_fooby has an invalid value"
+#endif
+*/
+
+ return 0;
+}
--- /dev/null
+
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// <memory_resource> feature macros
+
+/* Constant Value
+ __cpp_lib_memory_resource 201603L
+
+*/
+
+// XFAIL
+// #include <memory_resource>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**)
+{
+// ensure that the macros that are supposed to be defined in <memory_resource> are defined.
+
+/*
+#if !defined(__cpp_lib_fooby)
+# error "__cpp_lib_fooby is not defined"
+#elif __cpp_lib_fooby < 201606L
+# error "__cpp_lib_fooby has an invalid value"
+#endif
+*/
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cmath>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ unsigned int ui = -5;
+ ui = std::abs(ui); // expected-error {{call to 'abs' is ambiguous}}
+
+ unsigned char uc = -5;
+ uc = std::abs(uc); // expected-warning {{taking the absolute value of unsigned type 'unsigned char' has no effect}}
+
+ unsigned short us = -5;
+ us = std::abs(us); // expected-warning {{taking the absolute value of unsigned type 'unsigned short' has no effect}}
+
+ unsigned long ul = -5;
+ ul = std::abs(ul); // expected-error {{call to 'abs' is ambiguous}}
+
+ unsigned long long ull = -5;
+ ull = ::abs(ull); // expected-error {{call to 'abs' is ambiguous}}
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <string_view>
+
+// template <contiguous_iterator _It, sized_sentinel_for<_It> _End>
+// basic_string_view(_It, _End) -> basic_string_view<iter_value_t<_It>>;
+
+#include <string_view>
+#include <cassert>
+
+#include "make_string.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template<class CharT, class Sentinel>
+constexpr void test() {
+ auto val = MAKE_STRING_VIEW(CharT, "test");
+ auto sv = std::basic_string_view(val.begin(), Sentinel(val.end()));
+ ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view<CharT>);
+ assert(sv.size() == val.size());
+ assert(sv.data() == val.data());
+}
+
+constexpr void test() {
+ test<char, char*>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t, wchar_t*>();
+#endif
+ test<char8_t, char8_t*>();
+ test<char16_t, char16_t*>();
+ test<char32_t, char32_t*>();
+ test<char, const char*>();
+ test<char, sized_sentinel<const char*>>();
+}
+
+int main(int, char**) {
+ test();
+
+ return 0;
+}
+
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Throwing bad_any_cast is supported starting in macosx10.13
+// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}}
+
+// <any>
+
+// template <class ValueType>
+// ValueType any_cast(any const &);
+
+// Try and cast away const.
+
+#include <any>
+
+struct TestType {};
+struct TestType2 {};
+
+int main(int, char**)
+{
+ using std::any;
+ using std::any_cast;
+
+ any a;
+
+ // expected-error@any:* {{drops 'const' qualifier}}
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
+ any_cast<TestType &>(static_cast<any const&>(a)); // expected-note {{requested here}}
+
+ // expected-error@any:* {{cannot cast from lvalue of type 'const TestType' to rvalue reference type 'TestType &&'; types are not compatible}}
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
+ any_cast<TestType &&>(static_cast<any const&>(a)); // expected-note {{requested here}}
+
+ // expected-error@any:* {{drops 'const' qualifier}}
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
+ any_cast<TestType2 &>(static_cast<any const&&>(a)); // expected-note {{requested here}}
+
+ // expected-error@any:* {{cannot cast from lvalue of type 'const TestType2' to rvalue reference type 'TestType2 &&'; types are not compatible}}
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
+ any_cast<TestType2 &&>(static_cast<any const&&>(a)); // expected-note {{requested here}}
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Throwing bad_any_cast is supported starting in macosx10.13
+// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}}
+
+// <any>
+
+// template <class ValueType>
+// ValueType const any_cast(any const&);
+//
+// template <class ValueType>
+// ValueType any_cast(any &);
+//
+// template <class ValueType>
+// ValueType any_cast(any &&);
+
+// Test instantiating the any_cast with a non-copyable type.
+
+#include <any>
+
+using std::any;
+using std::any_cast;
+
+struct no_copy
+{
+ no_copy() {}
+ no_copy(no_copy &&) {}
+ no_copy(no_copy const &) = delete;
+};
+
+struct no_move {
+ no_move() {}
+ no_move(no_move&&) = delete;
+ no_move(no_move const&) {}
+};
+
+int main(int, char**) {
+ any a;
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be an lvalue reference or a CopyConstructible type"}}
+ // expected-error@any:* {{static_cast from 'no_copy' to 'no_copy' uses deleted function}}
+ any_cast<no_copy>(static_cast<any&>(a)); // expected-note {{requested here}}
+
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be a const lvalue reference or a CopyConstructible type"}}
+ // expected-error@any:* {{static_cast from 'const no_copy' to 'no_copy' uses deleted function}}
+ any_cast<no_copy>(static_cast<any const&>(a)); // expected-note {{requested here}}
+
+ any_cast<no_copy>(static_cast<any &&>(a)); // OK
+
+ // expected-error-re@any:* {{static_assert failed{{.*}} "ValueType is required to be an rvalue reference or a CopyConstructible type"}}
+ // expected-error@any:* {{static_cast from 'typename remove_reference<no_move &>::type' (aka 'no_move') to 'no_move' uses deleted function}}
+ any_cast<no_move>(static_cast<any &&>(a));
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <memory>
+
+// void declare_no_pointers(char* p, size_t n);
+// void undeclare_no_pointers(char* p, size_t n);
+
+#include <memory>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ char* p = new char[10];
+ std::declare_no_pointers(p, 10);
+ std::undeclare_no_pointers(p, 10);
+ delete [] p;
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// <memory>
+
+// void declare_reachable(void* p);
+// template <class T> T* undeclare_reachable(T* p);
+
+#include <memory>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ int* p = new int;
+ std::declare_reachable(p);
+ assert(std::undeclare_reachable(p) == p);
+ delete p;
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// pointer_safety get_pointer_safety();
+
+// UNSUPPORTED: c++03
+
+#include <memory>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ {
+ static_assert(std::is_enum<std::pointer_safety>::value, "");
+ static_assert(!std::is_convertible<std::pointer_safety, int>::value, "");
+ static_assert(std::is_same<
+ std::underlying_type<std::pointer_safety>::type,
+ unsigned char
+ >::value, "");
+ }
+ {
+ std::pointer_safety r = std::get_pointer_safety();
+ assert(r == std::pointer_safety::relaxed ||
+ r == std::pointer_safety::preferred ||
+ r == std::pointer_safety::strict);
+ }
+ // Regression test for https://llvm.org/PR26961
+ {
+ std::pointer_safety d;
+ d = std::get_pointer_safety();
+ assert(d == std::get_pointer_safety());
+ }
+
+ return 0;
+}
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX20_REMOVED_TYPE_TRAITS
+
+// type_traits
+
+// result_of
+
+#include <type_traits>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ [[maybe_unused]] std::result_of<int (*())()> a; // expected-warning {{'result_of<int (*())()>' is deprecated}}
+}
--- /dev/null
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUPPORT_COROUTINE_TYPES_H
+#define SUPPORT_COROUTINE_TYPES_H
+
+#include <experimental/coroutine>
+
+template <typename Ty> struct generator {
+ struct promise_type {
+ Ty current_value;
+ std::experimental::suspend_always yield_value(Ty value) {
+ this->current_value = value;
+ return {};
+ }
+ std::experimental::suspend_always initial_suspend() { return {}; }
+ std::experimental::suspend_always final_suspend() noexcept { return {}; }
+ generator get_return_object() { return generator{this}; };
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+
+ struct iterator {
+ std::experimental::coroutine_handle<promise_type> _Coro;
+ bool _Done;
+
+ iterator(std::experimental::coroutine_handle<promise_type> Coro, bool Done)
+ : _Coro(Coro), _Done(Done) {}
+
+ iterator &operator++() {
+ _Coro.resume();
+ _Done = _Coro.done();
+ return *this;
+ }
+
+ bool operator==(iterator const &_Right) const {
+ return _Done == _Right._Done;
+ }
+
+ bool operator!=(iterator const &_Right) const { return !(*this == _Right); }
+
+ Ty const &operator*() const { return _Coro.promise().current_value; }
+
+ Ty const *operator->() const { return &(operator*()); }
+ };
+
+ iterator begin() {
+ p.resume();
+ return {p, p.done()};
+ }
+
+ iterator end() { return {p, true}; }
+
+ generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }
+
+ ~generator() {
+ if (p)
+ p.destroy();
+ }
+
+private:
+ explicit generator(promise_type *p)
+ : p(std::experimental::coroutine_handle<promise_type>::from_promise(*p)) {}
+
+ std::experimental::coroutine_handle<promise_type> p;
+};
+
+#endif // SUPPORT_COROUTINE_TYPES_H
--- /dev/null
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef SUPPORT_TRACKED_VALUE_H
+#define SUPPORT_TRACKED_VALUE_H
+
+#include <cassert>
+
+#include "test_macros.h"
+
+struct TrackedValue {
+ enum State { CONSTRUCTED, MOVED_FROM, DESTROYED };
+ State state;
+
+ TrackedValue() : state(State::CONSTRUCTED) {}
+
+ TrackedValue(TrackedValue const& t) : state(State::CONSTRUCTED) {
+ assert(t.state != State::MOVED_FROM && "copying a moved-from object");
+ assert(t.state != State::DESTROYED && "copying a destroyed object");
+ }
+
+#if TEST_STD_VER >= 11
+ TrackedValue(TrackedValue&& t) : state(State::CONSTRUCTED) {
+ assert(t.state != State::MOVED_FROM && "double moving from an object");
+ assert(t.state != State::DESTROYED && "moving from a destroyed object");
+ t.state = State::MOVED_FROM;
+ }
+#endif
+
+ TrackedValue& operator=(TrackedValue const& t) {
+ assert(state != State::DESTROYED && "copy assigning into destroyed object");
+ assert(t.state != State::MOVED_FROM && "copying a moved-from object");
+ assert(t.state != State::DESTROYED && "copying a destroyed object");
+ state = t.state;
+ return *this;
+ }
+
+#if TEST_STD_VER >= 11
+ TrackedValue& operator=(TrackedValue&& t) {
+ assert(state != State::DESTROYED && "move assigning into destroyed object");
+ assert(t.state != State::MOVED_FROM && "double moving from an object");
+ assert(t.state != State::DESTROYED && "moving from a destroyed object");
+ state = t.state;
+ t.state = State::MOVED_FROM;
+ return *this;
+ }
+#endif
+
+ ~TrackedValue() {
+ assert(state != State::DESTROYED && "double-destroying an object");
+ state = State::DESTROYED;
+ }
+};
+
+#endif // SUPPORT_TRACKED_VALUE_H
--- /dev/null
+---
+Language: Cpp
+BasedOnStyle: Google
+PointerAlignment: Left
+...
--- /dev/null
+#!/usr/bin/env bash
+
+# Checkout LLVM sources
+git clone --depth=1 https://github.com/llvm/llvm-project.git llvm-project
+
+# Setup libc++ options
+if [ -z "$BUILD_32_BITS" ]; then
+ export BUILD_32_BITS=OFF && echo disabling 32 bit build
+fi
+
+# Build and install libc++ (Use unstable ABI for better sanitizer coverage)
+cd ./llvm-project
+cmake -DCMAKE_C_COMPILER=${C_COMPILER} \
+ -DCMAKE_CXX_COMPILER=${COMPILER} \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ -DCMAKE_INSTALL_PREFIX=/usr \
+ -DLIBCXX_ABI_UNSTABLE=OFF \
+ -DLLVM_USE_SANITIZER=${LIBCXX_SANITIZER} \
+ -DLLVM_BUILD_32_BITS=${BUILD_32_BITS} \
+ -DLLVM_ENABLE_PROJECTS='libcxx;libcxxabi' \
+ -S llvm -B llvm-build -G "Unix Makefiles"
+make -C llvm-build -j3 cxx cxxabi
+sudo make -C llvm-build install-cxx install-cxxabi
+cd ..
--- /dev/null
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG]"
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**System**
+Which OS, compiler, and compiler version are you using:
+ - OS:
+ - Compiler and version:
+
+**To reproduce**
+Steps to reproduce the behavior:
+1. sync to commit ...
+2. cmake/bazel...
+3. make ...
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
--- /dev/null
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[FR]"
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
--- /dev/null
+name: bazel
+
+on:
+ push: {}
+ pull_request: {}
+
+jobs:
+ build-and-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: mount bazel cache
+ uses: actions/cache@v2.0.0
+ env:
+ cache-name: bazel-cache
+ with:
+ path: "~/.cache/bazel"
+ key: ${{ env.cache-name }}-${{ runner.os }}-${{ github.ref }}
+ restore-keys: |
+ ${{ env.cache-name }}-${{ runner.os }}-main
+
+ - name: build
+ run: |
+ bazel build //:benchmark //:benchmark_main //test/...
+
+ - name: test
+ run: |
+ bazel test --test_output=all //test/...
--- /dev/null
+name: build-and-test-perfcounters
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ job:
+ # TODO(dominic): Extend this to include compiler and set through env: CC/CXX.
+ name: ${{ matrix.os }}.${{ matrix.build_type }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, ubuntu-16.04, ubuntu-20.04]
+ build_type: ['Release', 'Debug']
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: install libpfm
+ run: sudo apt install libpfm4-dev
+
+ - name: create build environment
+ run: cmake -E make_directory ${{ runner.workspace }}/_build
+
+ - name: configure cmake
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: cmake -DBENCHMARK_ENABLE_LIBPFM=1 -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+
+ - name: build
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: cmake --build . --config ${{ matrix.build_type }}
+
+ # Skip testing, for now. It seems perf_event_open does not succeed on the
+ # hosting machine, very likely a permissions issue.
+ # TODO(mtrofin): Enable test.
+ # - name: test
+ # shell: bash
+ # working-directory: ${{ runner.workspace }}/_build
+ # run: sudo ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure
--- /dev/null
+name: build-and-test
+
+on:
+ push: {}
+ pull_request: {}
+
+jobs:
+ # TODO: add 32-bit builds (g++ and clang++) for ubuntu
+ # (requires g++-multilib and libc6:i386)
+ # TODO: add coverage build (requires lcov)
+ # TODO: add clang + libc++ builds for ubuntu
+ # TODO: add clang + ubsan/asan/msan + libc++ builds for ubuntu
+ job:
+ name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.compiler }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, ubuntu-16.04, ubuntu-20.04, macos-latest]
+ build_type: ['Release', 'Debug']
+ compiler: [g++, clang++]
+ include:
+ - displayTargetName: windows-latest-release
+ os: windows-latest
+ build_type: 'Release'
+ - displayTargetName: windows-latest-debug
+ os: windows-latest
+ build_type: 'Debug'
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: create build environment
+ run: cmake -E make_directory ${{ runner.workspace }}/_build
+
+ - name: configure cmake
+ env:
+ CXX: ${{ matrix.compiler }}
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: >
+ cmake $GITHUB_WORKSPACE
+ -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+
+ - name: build
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: cmake --build . --config ${{ matrix.build_type }}
+
+ - name: test
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: ctest -C ${{ matrix.build_type }} -VV
+
+ ubuntu-14_04:
+ name: ubuntu-14.04.${{ matrix.build_type }}.${{ matrix.compiler }}
+ runs-on: [ubuntu-latest]
+ strategy:
+ fail-fast: false
+ matrix:
+ build_type: ['Release', 'Debug']
+ compiler: [g++-4.8, clang++-3.6]
+ include:
+ - compiler: g++-6
+ build_type: 'Debug'
+ run_tests: true
+ - compiler: g++-6
+ build_type: 'Release'
+ run_tests: true
+ container: ubuntu:14.04
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: install required bits
+ run: |
+ sudo apt update
+ sudo apt -y install clang-3.6 cmake3 g++-4.8 git
+
+ - name: install other bits
+ if: ${{ matrix.compiler }} == g++-6
+ run: |
+ sudo apt -y install software-properties-common
+ sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test"
+ sudo apt update
+ sudo apt -y install g++-6
+
+ - name: create build environment
+ run: cmake -E make_directory $GITHUB_WORKSPACE/_build
+
+ - name: configure cmake
+ env:
+ CXX: ${{ matrix.compiler }}
+ shell: bash
+ working-directory: ${{ github.workspace }}/_build
+ run: >
+ cmake $GITHUB_WORKSPACE
+ -DBENCHMARK_ENABLE_TESTING=${{ matrix.run_tests }}
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+ -DBENCHMARK_DOWNLOAD_DEPENDENCIES=${{ matrix.run_tests }}
+
+ - name: build
+ shell: bash
+ working-directory: ${{ github.workspace }}/_build
+ run: cmake --build . --config ${{ matrix.build_type }}
+
+ - name: test
+ if: ${{ matrix.run_tests }}
+ shell: bash
+ working-directory: ${{ github.workspace }}/_build
+ run: ctest -C ${{ matrix.build_type }} -VV
--- /dev/null
+name: pylint
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ pylint:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.8
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install pylint pylint-exit conan
+ - name: Run pylint
+ run: |
+ pylint `find . -name '*.py'|xargs` || pylint-exit $?
--- /dev/null
+name: sanitizer
+
+on:
+ push: {}
+ pull_request: {}
+
+env:
+ CC: clang
+ CXX: clang++
+ EXTRA_CXX_FLAGS: "-stdlib=libc++"
+ UBSAN_OPTIONS: "print_stacktrace=1"
+
+jobs:
+ job:
+ name: ${{ matrix.sanitizer }}.${{ matrix.build_type }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ build_type: ['Debug', 'RelWithDebInfo']
+ sanitizer: ['asan', 'ubsan', 'tsan']
+ # TODO: add 'msan' above. currently failing and needs investigation.
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: configure msan env
+ if: matrix.sanitizer == 'msan'
+ run: |
+ echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins" >> $GITHUB_ENV
+ echo "LIBCXX_SANITIZER=MemoryWithOrigins" >> $GITHUB_ENV
+
+ - name: configure ubsan env
+ if: matrix.sanitizer == 'ubsan'
+ run: |
+ echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all" >> $GITHUB_ENV
+ echo "LIBCXX_SANITIZER=Undefined" >> $GITHUB_ENV
+
+ - name: configure asan env
+ if: matrix.sanitizer == 'asan'
+ run: |
+ echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=address -fno-sanitize-recover=all" >> $GITHUB_ENV
+ echo "LIBCXX_SANITIZER=Address" >> $GITHUB_ENV
+
+ - name: configure tsan env
+ if: matrix.sanitizer == 'tsan'
+ run: |
+ echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all" >> $GITHUB_ENV
+ echo "LIBCXX_SANITIZER=Thread" >> $GITHUB_ENV
+
+ - name: install llvm stuff
+ run: "${GITHUB_WORKSPACE}/.github/.libcxx-setup.sh"
+
+ - name: create build environment
+ run: cmake -E make_directory ${{ runner.workspace }}/_build
+
+ - name: configure cmake
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: >
+ cmake $GITHUB_WORKSPACE
+ -DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF
+ -DBENCHMARK_ENABLE_LIBPFM=OFF
+ -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
+ -DCMAKE_C_COMPILER=${{ env.CC }}
+ -DCMAKE_CXX_COMPILER=${{ env.CXX }}
+ -DCMAKE_C_FLAGS="${{ env.EXTRA_FLAGS }}"
+ -DCMAKE_CXX_FLAGS="${{ env.EXTRA_FLAGS }} ${{ env.EXTRA_CXX_FLAGS }}"
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
+
+ - name: build
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: cmake --build . --config ${{ matrix.build_type }}
+
+ - name: test
+ shell: bash
+ working-directory: ${{ runner.workspace }}/_build
+ run: ctest -C ${{ matrix.build_type }} -VV
--- /dev/null
+name: test-bindings
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ python_bindings:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.8
+ - name: Install benchmark
+ run:
+ python setup.py install
+ - name: Run example bindings
+ run:
+ python bindings/python/google_benchmark/example.py
--- /dev/null
+*.a
+*.so
+*.so.?*
+*.dll
+*.exe
+*.dylib
+*.cmake
+!/cmake/*.cmake
+!/test/AssemblyTests.cmake
+*~
+*.swp
+*.pyc
+__pycache__
+
+# lcov
+*.lcov
+/lcov
+
+# cmake files.
+/Testing
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
+
+# makefiles.
+Makefile
+
+# in-source build.
+bin/
+lib/
+/test/*_test
+
+# exuberant ctags.
+tags
+
+# YouCompleteMe configuration.
+.ycm_extra_conf.pyc
+
+# ninja generated files.
+.ninja_deps
+.ninja_log
+build.ninja
+install_manifest.txt
+rules.ninja
+
+# bazel output symlinks.
+bazel-*
+
+# out-of-source build top-level folders.
+build/
+_build/
+build*/
+
+# in-source dependencies
+/googletest/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+CMakeSettings.json
+
+# Visual Studio Code cache/options directory
+.vscode/
+
+# Python build stuff
+dist/
+*.egg-info*
--- /dev/null
+sudo: required
+dist: trusty
+language: cpp
+
+matrix:
+ include:
+ - compiler: gcc
+ addons:
+ apt:
+ packages:
+ - lcov
+ env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Coverage
+ - compiler: gcc
+ addons:
+ apt:
+ packages:
+ - g++-multilib
+ - libc6:i386
+ env:
+ - COMPILER=g++
+ - C_COMPILER=gcc
+ - BUILD_TYPE=Debug
+ - BUILD_32_BITS=ON
+ - EXTRA_FLAGS="-m32"
+ - compiler: gcc
+ addons:
+ apt:
+ packages:
+ - g++-multilib
+ - libc6:i386
+ env:
+ - COMPILER=g++
+ - C_COMPILER=gcc
+ - BUILD_TYPE=Release
+ - BUILD_32_BITS=ON
+ - EXTRA_FLAGS="-m32"
+ - compiler: gcc
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=g++-6 C_COMPILER=gcc-6 BUILD_TYPE=Debug
+ - ENABLE_SANITIZER=1
+ - EXTRA_FLAGS="-fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fuse-ld=gold"
+ # Clang w/ libc++
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ clang-3.8
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
+ - LIBCXX_BUILD=1
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ clang-3.8
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
+ - LIBCXX_BUILD=1
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ # Clang w/ 32bit libc++
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ - clang-3.8
+ - g++-multilib
+ - libc6:i386
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
+ - LIBCXX_BUILD=1
+ - BUILD_32_BITS=ON
+ - EXTRA_FLAGS="-m32"
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ # Clang w/ 32bit libc++
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ - clang-3.8
+ - g++-multilib
+ - libc6:i386
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
+ - LIBCXX_BUILD=1
+ - BUILD_32_BITS=ON
+ - EXTRA_FLAGS="-m32"
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ # Clang w/ libc++, ASAN, UBSAN
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ clang-3.8
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
+ - LIBCXX_BUILD=1 LIBCXX_SANITIZER="Undefined;Address"
+ - ENABLE_SANITIZER=1
+ - EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=undefined,address -fno-sanitize-recover=all"
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ - UBSAN_OPTIONS=print_stacktrace=1
+ # Clang w/ libc++ and MSAN
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ clang-3.8
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
+ - LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
+ - ENABLE_SANITIZER=1
+ - EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ # Clang w/ libc++ and MSAN
+ - compiler: clang
+ dist: xenial
+ addons:
+ apt:
+ packages:
+ clang-3.8
+ env:
+ - INSTALL_GCC6_FROM_PPA=1
+ - COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
+ - LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
+ - ENABLE_SANITIZER=1
+ - EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
+ - EXTRA_CXX_FLAGS="-stdlib=libc++"
+ - os: osx
+ osx_image: xcode8.3
+ compiler: clang
+ env:
+ - COMPILER=clang++
+ - BUILD_TYPE=Release
+ - BUILD_32_BITS=ON
+ - EXTRA_FLAGS="-m32"
+
+before_script:
+ - if [ -n "${LIBCXX_BUILD}" ]; then
+ source .libcxx-setup.sh;
+ fi
+ - if [ -n "${ENABLE_SANITIZER}" ]; then
+ export EXTRA_OPTIONS="-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF";
+ else
+ export EXTRA_OPTIONS="";
+ fi
+ - mkdir -p build && cd build
+
+before_install:
+ - if [ -z "$BUILD_32_BITS" ]; then
+ export BUILD_32_BITS=OFF && echo disabling 32 bit build;
+ fi
+ - if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
+ sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test";
+ sudo apt-get update --option Acquire::Retries=100 --option Acquire::http::Timeout="60";
+ fi
+
+install:
+ - if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
+ travis_wait sudo -E apt-get -yq --no-install-suggests --no-install-recommends install g++-6;
+ fi
+ - if [ "${TRAVIS_OS_NAME}" == "linux" -a "${BUILD_32_BITS}" == "OFF" ]; then
+ travis_wait sudo -E apt-get -y --no-install-suggests --no-install-recommends install llvm-3.9-tools;
+ sudo cp /usr/lib/llvm-3.9/bin/FileCheck /usr/local/bin/;
+ fi
+ - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
+ PATH=~/.local/bin:${PATH};
+ pip install --user --upgrade pip;
+ travis_wait pip install --user cpp-coveralls;
+ fi
+ - if [ "${C_COMPILER}" == "gcc-7" -a "${TRAVIS_OS_NAME}" == "osx" ]; then
+ rm -f /usr/local/include/c++;
+ brew update;
+ travis_wait brew install gcc@7;
+ fi
+ - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
+ sudo apt-get update -qq;
+ sudo apt-get install -qq unzip cmake3;
+ wget https://github.com/bazelbuild/bazel/releases/download/3.2.0/bazel-3.2.0-installer-linux-x86_64.sh --output-document bazel-installer.sh;
+ travis_wait sudo bash bazel-installer.sh;
+ fi
+ - if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
+ curl -L -o bazel-installer.sh https://github.com/bazelbuild/bazel/releases/download/3.2.0/bazel-3.2.0-installer-darwin-x86_64.sh;
+ travis_wait sudo bash bazel-installer.sh;
+ fi
+
+script:
+ - cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_C_FLAGS="${EXTRA_FLAGS}" -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} ${EXTRA_CXX_FLAGS}" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_BUILD_32_BITS=${BUILD_32_BITS} ${EXTRA_OPTIONS} ..
+ - make
+ - ctest -C ${BUILD_TYPE} --output-on-failure
+ - bazel test -c dbg --define google_benchmark.have_regex=posix --announce_rc --verbose_failures --test_output=errors --keep_going //test/...
+
+after_success:
+ - if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
+ coveralls --include src --include include --gcov-options '\-lp' --root .. --build-root .;
+ fi
--- /dev/null
+import os
+import ycm_core
+
+# These are the compilation flags that will be used in case there's no
+# compilation database set (by default, one is not set).
+# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
+flags = [
+'-Wall',
+'-Werror',
+'-pedantic-errors',
+'-std=c++0x',
+'-fno-strict-aliasing',
+'-O3',
+'-DNDEBUG',
+# ...and the same thing goes for the magic -x option which specifies the
+# language that the files to be compiled are written in. This is mostly
+# relevant for c++ headers.
+# For a C project, you would set this to 'c' instead of 'c++'.
+'-x', 'c++',
+'-I', 'include',
+'-isystem', '/usr/include',
+'-isystem', '/usr/local/include',
+]
+
+
+# Set this to the absolute path to the folder (NOT the file!) containing the
+# compile_commands.json file to use that instead of 'flags'. See here for
+# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
+#
+# Most projects will NOT need to set this to anything; you can just change the
+# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
+compilation_database_folder = ''
+
+if os.path.exists( compilation_database_folder ):
+ database = ycm_core.CompilationDatabase( compilation_database_folder )
+else:
+ database = None
+
+SOURCE_EXTENSIONS = [ '.cc' ]
+
+def DirectoryOfThisScript():
+ return os.path.dirname( os.path.abspath( __file__ ) )
+
+
+def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
+ if not working_directory:
+ return list( flags )
+ new_flags = []
+ make_next_absolute = False
+ path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
+ for flag in flags:
+ new_flag = flag
+
+ if make_next_absolute:
+ make_next_absolute = False
+ if not flag.startswith( '/' ):
+ new_flag = os.path.join( working_directory, flag )
+
+ for path_flag in path_flags:
+ if flag == path_flag:
+ make_next_absolute = True
+ break
+
+ if flag.startswith( path_flag ):
+ path = flag[ len( path_flag ): ]
+ new_flag = path_flag + os.path.join( working_directory, path )
+ break
+
+ if new_flag:
+ new_flags.append( new_flag )
+ return new_flags
+
+
+def IsHeaderFile( filename ):
+ extension = os.path.splitext( filename )[ 1 ]
+ return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
+
+
+def GetCompilationInfoForFile( filename ):
+ # The compilation_commands.json file generated by CMake does not have entries
+ # for header files. So we do our best by asking the db for flags for a
+ # corresponding source file, if any. If one exists, the flags for that file
+ # should be good enough.
+ if IsHeaderFile( filename ):
+ basename = os.path.splitext( filename )[ 0 ]
+ for extension in SOURCE_EXTENSIONS:
+ replacement_file = basename + extension
+ if os.path.exists( replacement_file ):
+ compilation_info = database.GetCompilationInfoForFile(
+ replacement_file )
+ if compilation_info.compiler_flags_:
+ return compilation_info
+ return None
+ return database.GetCompilationInfoForFile( filename )
+
+
+def FlagsForFile( filename, **kwargs ):
+ if database:
+ # Bear in mind that compilation_info.compiler_flags_ does NOT return a
+ # python list, but a "list-like" StringVec object
+ compilation_info = GetCompilationInfoForFile( filename )
+ if not compilation_info:
+ return None
+
+ final_flags = MakeRelativePathsInFlagsAbsolute(
+ compilation_info.compiler_flags_,
+ compilation_info.compiler_working_dir_ )
+ else:
+ relative_to = DirectoryOfThisScript()
+ final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
+
+ return {
+ 'flags': final_flags,
+ 'do_cache': True
+ }
--- /dev/null
+# This is the official list of benchmark authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+#
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+#
+# Please keep the list sorted.
+
+Albert Pretorius <pretoalb@gmail.com>
+Alex Steele <steeleal123@gmail.com>
+Andriy Berestovskyy <berestovskyy@gmail.com>
+Arne Beer <arne@twobeer.de>
+Carto
+Christian Wassermann <christian_wassermann@web.de>
+Christopher Seymour <chris.j.seymour@hotmail.com>
+Colin Braley <braley.colin@gmail.com>
+Daniel Harvey <danielharvey458@gmail.com>
+David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
+Deniz Evrenci <denizevrenci@gmail.com>
+Dirac Research
+Dominik Czarnota <dominik.b.czarnota@gmail.com>
+Eric Backus <eric_backus@alum.mit.edu>
+Eric Fiselier <eric@efcs.ca>
+Eugene Zhuk <eugene.zhuk@gmail.com>
+Evgeny Safronov <division494@gmail.com>
+Federico Ficarelli <federico.ficarelli@gmail.com>
+Felix Homann <linuxaudio@showlabor.de>
+Gergő Szitár <szitar.gergo@gmail.com>
+Google Inc.
+International Business Machines Corporation
+Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
+Jern-Kuan Leong <jernkuan@gmail.com>
+JianXiong Zhou <zhoujianxiong2@gmail.com>
+Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
+Jordan Williams <jwillikers@protonmail.com>
+Jussi Knuuttila <jussi.knuuttila@gmail.com>
+Kaito Udagawa <umireon@gmail.com>
+Kishan Kumar <kumar.kishan@outlook.com>
+Lei Xu <eddyxu@gmail.com>
+Matt Clarkson <mattyclarkson@gmail.com>
+Maxim Vafin <maxvafin@gmail.com>
+MongoDB Inc.
+Nick Hutchinson <nshutchinson@gmail.com>
+Norman Heino <norman.heino@gmail.com>
+Oleksandr Sochka <sasha.sochka@gmail.com>
+Ori Livneh <ori.livneh@gmail.com>
+Paul Redmond <paul.redmond@gmail.com>
+Radoslav Yovchev <radoslav.tm@gmail.com>
+Roman Lebedev <lebedev.ri@gmail.com>
+Sayan Bhattacharjee <aero.sayan@gmail.com>
+Shuo Chen <chenshuo@chenshuo.com>
+Steinar H. Gunderson <sgunderson@bigfoot.com>
+Stripe, Inc.
+Tobias Schmidt <tobias.schmidt@in.tum.de>
+Yixuan Qiu <yixuanq@gmail.com>
+Yusuke Suzuki <utatane.tea@gmail.com>
+Zbigniew Skowron <zbychs@gmail.com>
+Min-Yih Hsu <yihshyng223@gmail.com>
--- /dev/null
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+licenses(["notice"])
+
+config_setting(
+ name = "windows",
+ values = {
+ "cpu": "x64_windows",
+ },
+ visibility = [":__subpackages__"],
+)
+
+cc_library(
+ name = "benchmark",
+ srcs = glob(
+ [
+ "src/*.cc",
+ "src/*.h",
+ ],
+ exclude = ["src/benchmark_main.cc"],
+ ),
+ hdrs = ["include/benchmark/benchmark.h"],
+ linkopts = select({
+ ":windows": ["-DEFAULTLIB:shlwapi.lib"],
+ "//conditions:default": ["-pthread"],
+ }),
+ strip_include_prefix = "include",
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "benchmark_main",
+ srcs = ["src/benchmark_main.cc"],
+ hdrs = ["include/benchmark/benchmark.h"],
+ strip_include_prefix = "include",
+ visibility = ["//visibility:public"],
+ deps = [":benchmark"],
+)
+
+cc_library(
+ name = "benchmark_internal_headers",
+ hdrs = glob(["src/*.h"]),
+ visibility = ["//test:__pkg__"],
+)
--- /dev/null
+cmake_minimum_required (VERSION 3.5.1)
+
+foreach(p
+ CMP0048 # OK to clear PROJECT_VERSION on project()
+ CMP0054 # CMake 3.1
+ CMP0056 # export EXE_LINKER_FLAGS to try_run
+ CMP0057 # Support no if() IN_LIST operator
+ CMP0063 # Honor visibility properties for all targets
+ CMP0077 # Allow option() overrides in importing projects
+ )
+ if(POLICY ${p})
+ cmake_policy(SET ${p} NEW)
+ endif()
+endforeach()
+
+project (benchmark VERSION 1.5.4 LANGUAGES CXX)
+
+option(BENCHMARK_ENABLE_TESTING "Enable testing of the benchmark library." ON)
+option(BENCHMARK_ENABLE_EXCEPTIONS "Enable the use of exceptions in the benchmark library." ON)
+option(BENCHMARK_ENABLE_LTO "Enable link time optimisation of the benchmark library." OFF)
+option(BENCHMARK_USE_LIBCXX "Build and test using libc++ as the standard library." OFF)
+if(NOT MSVC)
+ option(BENCHMARK_BUILD_32_BITS "Build a 32 bit version of the library." OFF)
+else()
+ set(BENCHMARK_BUILD_32_BITS OFF CACHE BOOL "Build a 32 bit version of the library - unsupported when using MSVC)" FORCE)
+endif()
+option(BENCHMARK_ENABLE_INSTALL "Enable installation of benchmark. (Projects embedding benchmark may want to turn this OFF.)" ON)
+
+# Allow unmet dependencies to be met using CMake's ExternalProject mechanics, which
+# may require downloading the source code.
+option(BENCHMARK_DOWNLOAD_DEPENDENCIES "Allow the downloading and in-tree building of unmet dependencies" OFF)
+
+# This option can be used to disable building and running unit tests which depend on gtest
+# in cases where it is not possible to build or find a valid version of gtest.
+option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" ON)
+
+option(BENCHMARK_ENABLE_LIBPFM "Enable performance counters provided by libpfm" OFF)
+
+set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+if(MSVC)
+ # As of CMake 3.18, CMAKE_SYSTEM_PROCESSOR is not set properly for MSVC and
+ # cross-compilation (e.g. Host=x86_64, target=aarch64) requires using the
+ # undocumented, but working variable.
+ # See https://gitlab.kitware.com/cmake/cmake/-/issues/15170
+ set(CMAKE_SYSTEM_PROCESSOR ${MSVC_CXX_ARCHITECTURE_ID})
+ if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM")
+ set(CMAKE_CROSSCOMPILING TRUE)
+ endif()
+endif()
+
+set(ENABLE_ASSEMBLY_TESTS_DEFAULT OFF)
+function(should_enable_assembly_tests)
+ if(CMAKE_BUILD_TYPE)
+ string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
+ if (${CMAKE_BUILD_TYPE_LOWER} MATCHES "coverage")
+ # FIXME: The --coverage flag needs to be removed when building assembly
+ # tests for this to work.
+ return()
+ endif()
+ endif()
+ if (MSVC)
+ return()
+ elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
+ return()
+ elseif(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
+ # FIXME: Make these work on 32 bit builds
+ return()
+ elseif(BENCHMARK_BUILD_32_BITS)
+ # FIXME: Make these work on 32 bit builds
+ return()
+ endif()
+ find_program(LLVM_FILECHECK_EXE FileCheck)
+ if (LLVM_FILECHECK_EXE)
+ set(LLVM_FILECHECK_EXE "${LLVM_FILECHECK_EXE}" CACHE PATH "llvm filecheck" FORCE)
+ message(STATUS "LLVM FileCheck Found: ${LLVM_FILECHECK_EXE}")
+ else()
+ message(STATUS "Failed to find LLVM FileCheck")
+ return()
+ endif()
+ set(ENABLE_ASSEMBLY_TESTS_DEFAULT ON PARENT_SCOPE)
+endfunction()
+should_enable_assembly_tests()
+
+# This option disables the building and running of the assembly verification tests
+option(BENCHMARK_ENABLE_ASSEMBLY_TESTS "Enable building and running the assembly tests"
+ ${ENABLE_ASSEMBLY_TESTS_DEFAULT})
+
+# Make sure we can import out CMake functions
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+
+# Read the git tags to determine the project version
+include(GetGitVersion)
+get_git_version(GIT_VERSION)
+
+# If no git version can be determined, use the version
+# from the project() command
+if ("${GIT_VERSION}" STREQUAL "0.0.0")
+ set(VERSION "${benchmark_VERSION}")
+else()
+ set(VERSION "${GIT_VERSION}")
+endif()
+# Tell the user what versions we are using
+message(STATUS "Version: ${VERSION}")
+
+# The version of the libraries
+set(GENERIC_LIB_VERSION ${VERSION})
+string(SUBSTRING ${VERSION} 0 1 GENERIC_LIB_SOVERSION)
+
+# Import our CMake modules
+include(CheckCXXCompilerFlag)
+include(AddCXXCompilerFlag)
+include(CXXFeatureCheck)
+
+if (BENCHMARK_BUILD_32_BITS)
+ add_required_cxx_compiler_flag(-m32)
+endif()
+
+if (MSVC)
+ # Turn compiler warnings up to 11
+ string(REGEX REPLACE "[-/]W[1-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+
+ if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
+ add_cxx_compiler_flag(-EHs-)
+ add_cxx_compiler_flag(-EHa-)
+ add_definitions(-D_HAS_EXCEPTIONS=0)
+ endif()
+ # Link time optimisation
+ if (BENCHMARK_ENABLE_LTO)
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")
+ set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
+
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /GL")
+ string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO}")
+ set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
+ string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
+ string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
+ set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
+
+ set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /GL")
+ set(CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL "${CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL} /LTCG")
+ set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /LTCG")
+ set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /LTCG")
+ endif()
+else()
+ # Try and enable C++11. Don't use C++14 because it doesn't work in some
+ # configurations.
+ add_cxx_compiler_flag(-std=c++11)
+ if (NOT HAVE_CXX_FLAG_STD_CXX11)
+ add_cxx_compiler_flag(-std=c++0x)
+ endif()
+
+ # Turn compiler warnings up to 11
+ add_cxx_compiler_flag(-Wall)
+ add_cxx_compiler_flag(-Wextra)
+ add_cxx_compiler_flag(-Wshadow)
+ add_cxx_compiler_flag(-Werror RELEASE)
+ add_cxx_compiler_flag(-Werror RELWITHDEBINFO)
+ add_cxx_compiler_flag(-Werror MINSIZEREL)
+ if (NOT BENCHMARK_ENABLE_TESTING)
+ # Disable warning when compiling tests as gtest does not use 'override'.
+ add_cxx_compiler_flag(-Wsuggest-override)
+ endif()
+ add_cxx_compiler_flag(-pedantic)
+ add_cxx_compiler_flag(-pedantic-errors)
+ add_cxx_compiler_flag(-Wshorten-64-to-32)
+ add_cxx_compiler_flag(-fstrict-aliasing)
+ # Disable warnings regarding deprecated parts of the library while building
+ # and testing those parts of the library.
+ add_cxx_compiler_flag(-Wno-deprecated-declarations)
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
+ # Intel silently ignores '-Wno-deprecated-declarations',
+ # warning no. 1786 must be explicitly disabled.
+ # See #631 for rationale.
+ add_cxx_compiler_flag(-wd1786)
+ endif()
+ # Disable deprecation warnings for release builds (when -Werror is enabled).
+ add_cxx_compiler_flag(-Wno-deprecated RELEASE)
+ add_cxx_compiler_flag(-Wno-deprecated RELWITHDEBINFO)
+ add_cxx_compiler_flag(-Wno-deprecated MINSIZEREL)
+ if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
+ add_cxx_compiler_flag(-fno-exceptions)
+ endif()
+
+ if (HAVE_CXX_FLAG_FSTRICT_ALIASING)
+ if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") #ICC17u2: Many false positives for Wstrict-aliasing
+ add_cxx_compiler_flag(-Wstrict-aliasing)
+ endif()
+ endif()
+ # ICC17u2: overloaded virtual function "benchmark::Fixture::SetUp" is only partially overridden
+ # (because of deprecated overload)
+ add_cxx_compiler_flag(-wd654)
+ add_cxx_compiler_flag(-Wthread-safety)
+ if (HAVE_CXX_FLAG_WTHREAD_SAFETY)
+ cxx_feature_check(THREAD_SAFETY_ATTRIBUTES)
+ endif()
+
+ # On most UNIX like platforms g++ and clang++ define _GNU_SOURCE as a
+ # predefined macro, which turns on all of the wonderful libc extensions.
+ # However g++ doesn't do this in Cygwin so we have to define it ourselfs
+ # since we depend on GNU/POSIX/BSD extensions.
+ if (CYGWIN)
+ add_definitions(-D_GNU_SOURCE=1)
+ endif()
+
+ if (QNXNTO)
+ add_definitions(-D_QNX_SOURCE)
+ endif()
+
+ # Link time optimisation
+ if (BENCHMARK_ENABLE_LTO)
+ add_cxx_compiler_flag(-flto)
+ add_cxx_compiler_flag(-Wno-lto-type-mismatch)
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ find_program(GCC_AR gcc-ar)
+ if (GCC_AR)
+ set(CMAKE_AR ${GCC_AR})
+ endif()
+ find_program(GCC_RANLIB gcc-ranlib)
+ if (GCC_RANLIB)
+ set(CMAKE_RANLIB ${GCC_RANLIB})
+ endif()
+ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+ include(llvm-toolchain)
+ endif()
+ endif()
+
+ # Coverage build type
+ set(BENCHMARK_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG}"
+ CACHE STRING "Flags used by the C++ compiler during coverage builds."
+ FORCE)
+ set(BENCHMARK_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_DEBUG}"
+ CACHE STRING "Flags used for linking binaries during coverage builds."
+ FORCE)
+ set(BENCHMARK_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}"
+ CACHE STRING "Flags used by the shared libraries linker during coverage builds."
+ FORCE)
+ mark_as_advanced(
+ BENCHMARK_CXX_FLAGS_COVERAGE
+ BENCHMARK_EXE_LINKER_FLAGS_COVERAGE
+ BENCHMARK_SHARED_LINKER_FLAGS_COVERAGE)
+ set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
+ "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage.")
+ add_cxx_compiler_flag(--coverage COVERAGE)
+endif()
+
+if (BENCHMARK_USE_LIBCXX)
+ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+ add_cxx_compiler_flag(-stdlib=libc++)
+ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
+ "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
+ add_cxx_compiler_flag(-nostdinc++)
+ message(WARNING "libc++ header path must be manually specified using CMAKE_CXX_FLAGS")
+ # Adding -nodefaultlibs directly to CMAKE_<TYPE>_LINKER_FLAGS will break
+ # configuration checks such as 'find_package(Threads)'
+ list(APPEND BENCHMARK_CXX_LINKER_FLAGS -nodefaultlibs)
+ # -lc++ cannot be added directly to CMAKE_<TYPE>_LINKER_FLAGS because
+ # linker flags appear before all linker inputs and -lc++ must appear after.
+ list(APPEND BENCHMARK_CXX_LIBRARIES c++)
+ else()
+ message(FATAL_ERROR "-DBENCHMARK_USE_LIBCXX:BOOL=ON is not supported for compiler")
+ endif()
+endif(BENCHMARK_USE_LIBCXX)
+
+set(EXTRA_CXX_FLAGS "")
+if (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+ # Clang on Windows fails to compile the regex feature check under C++11
+ set(EXTRA_CXX_FLAGS "-DCMAKE_CXX_STANDARD=14")
+endif()
+
+# C++ feature checks
+# Determine the correct regular expression engine to use
+cxx_feature_check(STD_REGEX ${EXTRA_CXX_FLAGS})
+cxx_feature_check(GNU_POSIX_REGEX ${EXTRA_CXX_FLAGS})
+cxx_feature_check(POSIX_REGEX ${EXTRA_CXX_FLAGS})
+if(NOT HAVE_STD_REGEX AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
+ message(FATAL_ERROR "Failed to determine the source files for the regular expression backend")
+endif()
+if (NOT BENCHMARK_ENABLE_EXCEPTIONS AND HAVE_STD_REGEX
+ AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
+ message(WARNING "Using std::regex with exceptions disabled is not fully supported")
+endif()
+
+cxx_feature_check(STEADY_CLOCK)
+# Ensure we have pthreads
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+find_package(Threads REQUIRED)
+
+if (BENCHMARK_ENABLE_LIBPFM)
+ find_package(PFM)
+endif()
+
+# Set up directories
+include_directories(${PROJECT_SOURCE_DIR}/include)
+
+# Build the targets
+add_subdirectory(src)
+
+if (BENCHMARK_ENABLE_TESTING)
+ enable_testing()
+ if (BENCHMARK_ENABLE_GTEST_TESTS AND
+ NOT (TARGET gtest AND TARGET gtest_main AND
+ TARGET gmock AND TARGET gmock_main))
+ include(GoogleTest)
+ endif()
+ add_subdirectory(test)
+endif()
--- /dev/null
+# How to contribute #
+
+We'd love to accept your patches and contributions to this project. There are
+a just a few small guidelines you need to follow.
+
+
+## Contributor License Agreement ##
+
+Contributions to any Google project must be accompanied by a Contributor
+License Agreement. This is not a copyright **assignment**, it simply gives
+Google permission to use and redistribute your contributions as part of the
+project.
+
+ * If you are an individual writing original source code and you're sure you
+ own the intellectual property, then you'll need to sign an [individual
+ CLA][].
+
+ * If you work for a company that wants to allow you to contribute your work,
+ then you'll need to sign a [corporate CLA][].
+
+You generally only need to submit a CLA once, so if you've already submitted
+one (even if it was for a different project), you probably don't need to do it
+again.
+
+[individual CLA]: https://developers.google.com/open-source/cla/individual
+[corporate CLA]: https://developers.google.com/open-source/cla/corporate
+
+Once your CLA is submitted (or if you already submitted one for
+another Google project), make a commit adding yourself to the
+[AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part
+of your first [pull request][].
+
+[AUTHORS]: AUTHORS
+[CONTRIBUTORS]: CONTRIBUTORS
+
+
+## Submitting a patch ##
+
+ 1. It's generally best to start by opening a new issue describing the bug or
+ feature you're intending to fix. Even if you think it's relatively minor,
+ it's helpful to know what people are working on. Mention in the initial
+ issue that you are planning to work on that bug or feature so that it can
+ be assigned to you.
+
+ 1. Follow the normal process of [forking][] the project, and setup a new
+ branch to work in. It's important that each group of changes be done in
+ separate branches in order to ensure that a pull request only includes the
+ commits related to that bug or feature.
+
+ 1. Do your best to have [well-formed commit messages][] for each change.
+ This provides consistency throughout the project, and ensures that commit
+ messages are able to be formatted properly by various git tools.
+
+ 1. Finally, push the commits to your fork and submit a [pull request][].
+
+[forking]: https://help.github.com/articles/fork-a-repo
+[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
+[pull request]: https://help.github.com/articles/creating-a-pull-request
--- /dev/null
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# Names should be added to this file only after verifying that
+# the individual or the individual's organization has agreed to
+# the appropriate Contributor License Agreement, found here:
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# The agreement for individuals can be filled out on the web.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file, depending on whether the
+# individual or corporate CLA was used.
+#
+# Names should be added to this file as:
+# Name <email address>
+#
+# Please keep the list sorted.
+
+Abhina Sreeskantharajan <abhina.sreeskantharajan@ibm.com>
+Albert Pretorius <pretoalb@gmail.com>
+Alex Steele <steelal123@gmail.com>
+Andriy Berestovskyy <berestovskyy@gmail.com>
+Arne Beer <arne@twobeer.de>
+Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
+Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
+Christian Wassermann <christian_wassermann@web.de>
+Christopher Seymour <chris.j.seymour@hotmail.com>
+Colin Braley <braley.colin@gmail.com>
+Cyrille Faucheux <cyrille.faucheux@gmail.com>
+Daniel Harvey <danielharvey458@gmail.com>
+David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
+Deniz Evrenci <denizevrenci@gmail.com>
+Dominic Hamon <dma@stripysock.com> <dominic@google.com>
+Dominik Czarnota <dominik.b.czarnota@gmail.com>
+Eric Backus <eric_backus@alum.mit.edu>
+Eric Fiselier <eric@efcs.ca>
+Eugene Zhuk <eugene.zhuk@gmail.com>
+Evgeny Safronov <division494@gmail.com>
+Fanbo Meng <fanbo.meng@ibm.com>
+Federico Ficarelli <federico.ficarelli@gmail.com>
+Felix Homann <linuxaudio@showlabor.de>
+Geoffrey Martin-Noble <gcmn@google.com> <gmngeoffrey@gmail.com>
+Gergő Szitár <szitar.gergo@gmail.com>
+Hannes Hauswedell <h2@fsfe.org>
+Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
+Jern-Kuan Leong <jernkuan@gmail.com>
+JianXiong Zhou <zhoujianxiong2@gmail.com>
+Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
+John Millikin <jmillikin@stripe.com>
+Jordan Williams <jwillikers@protonmail.com>
+Jussi Knuuttila <jussi.knuuttila@gmail.com>
+Kai Wolf <kai.wolf@gmail.com>
+Kaito Udagawa <umireon@gmail.com>
+Kishan Kumar <kumar.kishan@outlook.com>
+Lei Xu <eddyxu@gmail.com>
+Matt Clarkson <mattyclarkson@gmail.com>
+Maxim Vafin <maxvafin@gmail.com>
+Nick Hutchinson <nshutchinson@gmail.com>
+Norman Heino <norman.heino@gmail.com>
+Oleksandr Sochka <sasha.sochka@gmail.com>
+Ori Livneh <ori.livneh@gmail.com>
+Pascal Leroy <phl@google.com>
+Paul Redmond <paul.redmond@gmail.com>
+Pierre Phaneuf <pphaneuf@google.com>
+Radoslav Yovchev <radoslav.tm@gmail.com>
+Raul Marin <rmrodriguez@cartodb.com>
+Ray Glover <ray.glover@uk.ibm.com>
+Robert Guo <robert.guo@mongodb.com>
+Roman Lebedev <lebedev.ri@gmail.com>
+Sayan Bhattacharjee <aero.sayan@gmail.com>
+Shuo Chen <chenshuo@chenshuo.com>
+Steven Wan <wan.yu@ibm.com>
+Tobias Schmidt <tobias.schmidt@in.tum.de>
+Tobias Ulvgård <tobias.ulvgard@dirac.se>
+Tom Madams <tom.ej.madams@gmail.com> <tmadams@google.com>
+Yixuan Qiu <yixuanq@gmail.com>
+Yusuke Suzuki <utatane.tea@gmail.com>
+Zbigniew Skowron <zbychs@gmail.com>
+Min-Yih Hsu <yihshyng223@gmail.com>
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null
+# Benchmark
+
+[![build-and-test](https://github.com/google/benchmark/workflows/build-and-test/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Abuild-and-test)
+[![bazel](https://github.com/google/benchmark/actions/workflows/bazel.yml/badge.svg)](https://github.com/google/benchmark/actions/workflows/bazel.yml)
+[![pylint](https://github.com/google/benchmark/workflows/pylint/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Apylint)
+[![test-bindings](https://github.com/google/benchmark/workflows/test-bindings/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Atest-bindings)
+
+[![Build Status](https://travis-ci.org/google/benchmark.svg?branch=master)](https://travis-ci.org/google/benchmark)
+[![Coverage Status](https://coveralls.io/repos/google/benchmark/badge.svg)](https://coveralls.io/r/google/benchmark)
+
+
+A library to benchmark code snippets, similar to unit tests. Example:
+
+```c++
+#include <benchmark/benchmark.h>
+
+static void BM_SomeFunction(benchmark::State& state) {
+ // Perform setup here
+ for (auto _ : state) {
+ // This code gets timed
+ SomeFunction();
+ }
+}
+// Register the function as a benchmark
+BENCHMARK(BM_SomeFunction);
+// Run the benchmark
+BENCHMARK_MAIN();
+```
+
+To get started, see [Requirements](#requirements) and
+[Installation](#installation). See [Usage](#usage) for a full example and the
+[User Guide](#user-guide) for a more comprehensive feature overview.
+
+It may also help to read the [Google Test documentation](https://github.com/google/googletest/blob/master/docs/primer.md)
+as some of the structural aspects of the APIs are similar.
+
+### Resources
+
+[Discussion group](https://groups.google.com/d/forum/benchmark-discuss)
+
+IRC channels:
+* [libera](https://libera.chat) #benchmark
+
+[Additional Tooling Documentation](docs/tools.md)
+
+[Assembly Testing Documentation](docs/AssemblyTests.md)
+
+## Requirements
+
+The library can be used with C++03. However, it requires C++11 to build,
+including compiler and standard library support.
+
+The following minimum versions are required to build the library:
+
+* GCC 4.8
+* Clang 3.4
+* Visual Studio 14 2015
+* Intel 2015 Update 1
+
+See [Platform-Specific Build Instructions](#platform-specific-build-instructions).
+
+## Installation
+
+This describes the installation process using cmake. As pre-requisites, you'll
+need git and cmake installed.
+
+_See [dependencies.md](dependencies.md) for more details regarding supported
+versions of build tools._
+
+```bash
+# Check out the library.
+$ git clone https://github.com/google/benchmark.git
+# Benchmark requires Google Test as a dependency. Add the source tree as a subdirectory.
+$ git clone https://github.com/google/googletest.git benchmark/googletest
+# Go to the library root directory
+$ cd benchmark
+# Make a build directory to place the build output.
+$ cmake -E make_directory "build"
+# Generate build system files with cmake.
+$ cmake -E chdir "build" cmake -DCMAKE_BUILD_TYPE=Release ../
+# or, starting with CMake 3.13, use a simpler form:
+# cmake -DCMAKE_BUILD_TYPE=Release -S . -B "build"
+# Build the library.
+$ cmake --build "build" --config Release
+```
+This builds the `benchmark` and `benchmark_main` libraries and tests.
+On a unix system, the build directory should now look something like this:
+
+```
+/benchmark
+ /build
+ /src
+ /libbenchmark.a
+ /libbenchmark_main.a
+ /test
+ ...
+```
+
+Next, you can run the tests to check the build.
+
+```bash
+$ cmake -E chdir "build" ctest --build-config Release
+```
+
+If you want to install the library globally, also run:
+
+```
+sudo cmake --build "build" --config Release --target install
+```
+
+Note that Google Benchmark requires Google Test to build and run the tests. This
+dependency can be provided two ways:
+
+* Checkout the Google Test sources into `benchmark/googletest` as above.
+* Otherwise, if `-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON` is specified during
+ configuration, the library will automatically download and build any required
+ dependencies.
+
+If you do not wish to build and run the tests, add `-DBENCHMARK_ENABLE_GTEST_TESTS=OFF`
+to `CMAKE_ARGS`.
+
+### Debug vs Release
+
+By default, benchmark builds as a debug library. You will see a warning in the
+output when this is the case. To build it as a release library instead, add
+`-DCMAKE_BUILD_TYPE=Release` when generating the build system files, as shown
+above. The use of `--config Release` in build commands is needed to properly
+support multi-configuration tools (like Visual Studio for example) and can be
+skipped for other build systems (like Makefile).
+
+To enable link-time optimisation, also add `-DBENCHMARK_ENABLE_LTO=true` when
+generating the build system files.
+
+If you are using gcc, you might need to set `GCC_AR` and `GCC_RANLIB` cmake
+cache variables, if autodetection fails.
+
+If you are using clang, you may need to set `LLVMAR_EXECUTABLE`,
+`LLVMNM_EXECUTABLE` and `LLVMRANLIB_EXECUTABLE` cmake cache variables.
+
+### Stable and Experimental Library Versions
+
+The main branch contains the latest stable version of the benchmarking library;
+the API of which can be considered largely stable, with source breaking changes
+being made only upon the release of a new major version.
+
+Newer, experimental, features are implemented and tested on the
+[`v2` branch](https://github.com/google/benchmark/tree/v2). Users who wish
+to use, test, and provide feedback on the new features are encouraged to try
+this branch. However, this branch provides no stability guarantees and reserves
+the right to change and break the API at any time.
+
+## Usage
+
+### Basic usage
+
+Define a function that executes the code to measure, register it as a benchmark
+function using the `BENCHMARK` macro, and ensure an appropriate `main` function
+is available:
+
+```c++
+#include <benchmark/benchmark.h>
+
+static void BM_StringCreation(benchmark::State& state) {
+ for (auto _ : state)
+ std::string empty_string;
+}
+// Register the function as a benchmark
+BENCHMARK(BM_StringCreation);
+
+// Define another benchmark
+static void BM_StringCopy(benchmark::State& state) {
+ std::string x = "hello";
+ for (auto _ : state)
+ std::string copy(x);
+}
+BENCHMARK(BM_StringCopy);
+
+BENCHMARK_MAIN();
+```
+
+To run the benchmark, compile and link against the `benchmark` library
+(libbenchmark.a/.so). If you followed the build steps above, this library will
+be under the build directory you created.
+
+```bash
+# Example on linux after running the build steps above. Assumes the
+# `benchmark` and `build` directories are under the current directory.
+$ g++ mybenchmark.cc -std=c++11 -isystem benchmark/include \
+ -Lbenchmark/build/src -lbenchmark -lpthread -o mybenchmark
+```
+
+Alternatively, link against the `benchmark_main` library and remove
+`BENCHMARK_MAIN();` above to get the same behavior.
+
+The compiled executable will run all benchmarks by default. Pass the `--help`
+flag for option information or see the guide below.
+
+### Usage with CMake
+
+If using CMake, it is recommended to link against the project-provided
+`benchmark::benchmark` and `benchmark::benchmark_main` targets using
+`target_link_libraries`.
+It is possible to use ```find_package``` to import an installed version of the
+library.
+```cmake
+find_package(benchmark REQUIRED)
+```
+Alternatively, ```add_subdirectory``` will incorporate the library directly in
+to one's CMake project.
+```cmake
+add_subdirectory(benchmark)
+```
+Either way, link to the library as follows.
+```cmake
+target_link_libraries(MyTarget benchmark::benchmark)
+```
+
+## Platform Specific Build Instructions
+
+### Building with GCC
+
+When the library is built using GCC it is necessary to link with the pthread
+library due to how GCC implements `std::thread`. Failing to link to pthread will
+lead to runtime exceptions (unless you're using libc++), not linker errors. See
+[issue #67](https://github.com/google/benchmark/issues/67) for more details. You
+can link to pthread by adding `-pthread` to your linker command. Note, you can
+also use `-lpthread`, but there are potential issues with ordering of command
+line parameters if you use that.
+
+### Building with Visual Studio 2015 or 2017
+
+The `shlwapi` library (`-lshlwapi`) is required to support a call to `CPUInfo` which reads the registry. Either add `shlwapi.lib` under `[ Configuration Properties > Linker > Input ]`, or use the following:
+
+```
+// Alternatively, can add libraries using linker options.
+#ifdef _WIN32
+#pragma comment ( lib, "Shlwapi.lib" )
+#ifdef _DEBUG
+#pragma comment ( lib, "benchmarkd.lib" )
+#else
+#pragma comment ( lib, "benchmark.lib" )
+#endif
+#endif
+```
+
+Can also use the graphical version of CMake:
+* Open `CMake GUI`.
+* Under `Where to build the binaries`, same path as source plus `build`.
+* Under `CMAKE_INSTALL_PREFIX`, same path as source plus `install`.
+* Click `Configure`, `Generate`, `Open Project`.
+* If build fails, try deleting entire directory and starting again, or unticking options to build less.
+
+### Building with Intel 2015 Update 1 or Intel System Studio Update 4
+
+See instructions for building with Visual Studio. Once built, right click on the solution and change the build to Intel.
+
+### Building on Solaris
+
+If you're running benchmarks on solaris, you'll want the kstat library linked in
+too (`-lkstat`).
+
+## User Guide
+
+### Command Line
+
+[Output Formats](#output-formats)
+
+[Output Files](#output-files)
+
+[Running Benchmarks](#running-benchmarks)
+
+[Running a Subset of Benchmarks](#running-a-subset-of-benchmarks)
+
+[Result Comparison](#result-comparison)
+
+[Extra Context](#extra-context)
+
+### Library
+
+[Runtime and Reporting Considerations](#runtime-and-reporting-considerations)
+
+[Passing Arguments](#passing-arguments)
+
+[Custom Benchmark Name](#custom-benchmark-name)
+
+[Calculating Asymptotic Complexity](#asymptotic-complexity)
+
+[Templated Benchmarks](#templated-benchmarks)
+
+[Fixtures](#fixtures)
+
+[Custom Counters](#custom-counters)
+
+[Multithreaded Benchmarks](#multithreaded-benchmarks)
+
+[CPU Timers](#cpu-timers)
+
+[Manual Timing](#manual-timing)
+
+[Setting the Time Unit](#setting-the-time-unit)
+
+[Random Interleaving](docs/random_interleaving.md)
+
+[User-Requested Performance Counters](docs/perf_counters.md)
+
+[Preventing Optimization](#preventing-optimization)
+
+[Reporting Statistics](#reporting-statistics)
+
+[Custom Statistics](#custom-statistics)
+
+[Using RegisterBenchmark](#using-register-benchmark)
+
+[Exiting with an Error](#exiting-with-an-error)
+
+[A Faster KeepRunning Loop](#a-faster-keep-running-loop)
+
+[Disabling CPU Frequency Scaling](#disabling-cpu-frequency-scaling)
+
+
+<a name="output-formats" />
+
+### Output Formats
+
+The library supports multiple output formats. Use the
+`--benchmark_format=<console|json|csv>` flag (or set the
+`BENCHMARK_FORMAT=<console|json|csv>` environment variable) to set
+the format type. `console` is the default format.
+
+The Console format is intended to be a human readable format. By default
+the format generates color output. Context is output on stderr and the
+tabular data on stdout. Example tabular output looks like:
+
+```
+Benchmark Time(ns) CPU(ns) Iterations
+----------------------------------------------------------------------
+BM_SetInsert/1024/1 28928 29349 23853 133.097kB/s 33.2742k items/s
+BM_SetInsert/1024/8 32065 32913 21375 949.487kB/s 237.372k items/s
+BM_SetInsert/1024/10 33157 33648 21431 1.13369MB/s 290.225k items/s
+```
+
+The JSON format outputs human readable json split into two top level attributes.
+The `context` attribute contains information about the run in general, including
+information about the CPU and the date.
+The `benchmarks` attribute contains a list of every benchmark run. Example json
+output looks like:
+
+```json
+{
+ "context": {
+ "date": "2015/03/17-18:40:25",
+ "num_cpus": 40,
+ "mhz_per_cpu": 2801,
+ "cpu_scaling_enabled": false,
+ "build_type": "debug"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_SetInsert/1024/1",
+ "iterations": 94877,
+ "real_time": 29275,
+ "cpu_time": 29836,
+ "bytes_per_second": 134066,
+ "items_per_second": 33516
+ },
+ {
+ "name": "BM_SetInsert/1024/8",
+ "iterations": 21609,
+ "real_time": 32317,
+ "cpu_time": 32429,
+ "bytes_per_second": 986770,
+ "items_per_second": 246693
+ },
+ {
+ "name": "BM_SetInsert/1024/10",
+ "iterations": 21393,
+ "real_time": 32724,
+ "cpu_time": 33355,
+ "bytes_per_second": 1199226,
+ "items_per_second": 299807
+ }
+ ]
+}
+```
+
+The CSV format outputs comma-separated values. The `context` is output on stderr
+and the CSV itself on stdout. Example CSV output looks like:
+
+```
+name,iterations,real_time,cpu_time,bytes_per_second,items_per_second,label
+"BM_SetInsert/1024/1",65465,17890.7,8407.45,475768,118942,
+"BM_SetInsert/1024/8",116606,18810.1,9766.64,3.27646e+06,819115,
+"BM_SetInsert/1024/10",106365,17238.4,8421.53,4.74973e+06,1.18743e+06,
+```
+
+<a name="output-files" />
+
+### Output Files
+
+Write benchmark results to a file with the `--benchmark_out=<filename>` option
+(or set `BENCHMARK_OUT`). Specify the output format with
+`--benchmark_out_format={json|console|csv}` (or set
+`BENCHMARK_OUT_FORMAT={json|console|csv}`). Note that the 'csv' reporter is
+deprecated and the saved `.csv` file
+[is not parsable](https://github.com/google/benchmark/issues/794) by csv
+parsers.
+
+Specifying `--benchmark_out` does not suppress the console output.
+
+<a name="running-benchmarks" />
+
+### Running Benchmarks
+
+Benchmarks are executed by running the produced binaries. Benchmarks binaries,
+by default, accept options that may be specified either through their command
+line interface or by setting environment variables before execution. For every
+`--option_flag=<value>` CLI switch, a corresponding environment variable
+`OPTION_FLAG=<value>` exist and is used as default if set (CLI switches always
+ prevails). A complete list of CLI options is available running benchmarks
+ with the `--help` switch.
+
+<a name="running-a-subset-of-benchmarks" />
+
+### Running a Subset of Benchmarks
+
+The `--benchmark_filter=<regex>` option (or `BENCHMARK_FILTER=<regex>`
+environment variable) can be used to only run the benchmarks that match
+the specified `<regex>`. For example:
+
+```bash
+$ ./run_benchmarks.x --benchmark_filter=BM_memcpy/32
+Run on (1 X 2300 MHz CPU )
+2016-06-25 19:34:24
+Benchmark Time CPU Iterations
+----------------------------------------------------
+BM_memcpy/32 11 ns 11 ns 79545455
+BM_memcpy/32k 2181 ns 2185 ns 324074
+BM_memcpy/32 12 ns 12 ns 54687500
+BM_memcpy/32k 1834 ns 1837 ns 357143
+```
+
+<a name="result-comparison" />
+
+### Result comparison
+
+It is possible to compare the benchmarking results.
+See [Additional Tooling Documentation](docs/tools.md)
+
+<a name="extra-context" />
+
+### Extra Context
+
+Sometimes it's useful to add extra context to the content printed before the
+results. By default this section includes information about the CPU on which
+the benchmarks are running. If you do want to add more context, you can use
+the `benchmark_context` command line flag:
+
+```bash
+$ ./run_benchmarks --benchmark_context=pwd=`pwd`
+Run on (1 x 2300 MHz CPU)
+pwd: /home/user/benchmark/
+Benchmark Time CPU Iterations
+----------------------------------------------------
+BM_memcpy/32 11 ns 11 ns 79545455
+BM_memcpy/32k 2181 ns 2185 ns 324074
+```
+
+You can get the same effect with the API:
+
+```c++
+ benchmark::AddCustomContext("foo", "bar");
+```
+
+Note that attempts to add a second value with the same key will fail with an
+error message.
+
+<a name="runtime-and-reporting-considerations" />
+
+### Runtime and Reporting Considerations
+
+When the benchmark binary is executed, each benchmark function is run serially.
+The number of iterations to run is determined dynamically by running the
+benchmark a few times and measuring the time taken and ensuring that the
+ultimate result will be statistically stable. As such, faster benchmark
+functions will be run for more iterations than slower benchmark functions, and
+the number of iterations is thus reported.
+
+In all cases, the number of iterations for which the benchmark is run is
+governed by the amount of time the benchmark takes. Concretely, the number of
+iterations is at least one, not more than 1e9, until CPU time is greater than
+the minimum time, or the wallclock time is 5x minimum time. The minimum time is
+set per benchmark by calling `MinTime` on the registered benchmark object.
+
+Average timings are then reported over the iterations run. If multiple
+repetitions are requested using the `--benchmark_repetitions` command-line
+option, or at registration time, the benchmark function will be run several
+times and statistical results across these repetitions will also be reported.
+
+As well as the per-benchmark entries, a preamble in the report will include
+information about the machine on which the benchmarks are run.
+
+<a name="passing-arguments" />
+
+### Passing Arguments
+
+Sometimes a family of benchmarks can be implemented with just one routine that
+takes an extra argument to specify which one of the family of benchmarks to
+run. For example, the following code defines a family of benchmarks for
+measuring the speed of `memcpy()` calls of different lengths:
+
+```c++
+static void BM_memcpy(benchmark::State& state) {
+ char* src = new char[state.range(0)];
+ char* dst = new char[state.range(0)];
+ memset(src, 'x', state.range(0));
+ for (auto _ : state)
+ memcpy(dst, src, state.range(0));
+ state.SetBytesProcessed(int64_t(state.iterations()) *
+ int64_t(state.range(0)));
+ delete[] src;
+ delete[] dst;
+}
+BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
+```
+
+The preceding code is quite repetitive, and can be replaced with the following
+short-hand. The following invocation will pick a few appropriate arguments in
+the specified range and will generate a benchmark for each such argument.
+
+```c++
+BENCHMARK(BM_memcpy)->Range(8, 8<<10);
+```
+
+By default the arguments in the range are generated in multiples of eight and
+the command above selects [ 8, 64, 512, 4k, 8k ]. In the following code the
+range multiplier is changed to multiples of two.
+
+```c++
+BENCHMARK(BM_memcpy)->RangeMultiplier(2)->Range(8, 8<<10);
+```
+
+Now arguments generated are [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].
+
+The preceding code shows a method of defining a sparse range. The following
+example shows a method of defining a dense range. It is then used to benchmark
+the performance of `std::vector` initialization for uniformly increasing sizes.
+
+```c++
+static void BM_DenseRange(benchmark::State& state) {
+ for(auto _ : state) {
+ std::vector<int> v(state.range(0), state.range(0));
+ benchmark::DoNotOptimize(v.data());
+ benchmark::ClobberMemory();
+ }
+}
+BENCHMARK(BM_DenseRange)->DenseRange(0, 1024, 128);
+```
+
+Now arguments generated are [ 0, 128, 256, 384, 512, 640, 768, 896, 1024 ].
+
+You might have a benchmark that depends on two or more inputs. For example, the
+following code defines a family of benchmarks for measuring the speed of set
+insertion.
+
+```c++
+static void BM_SetInsert(benchmark::State& state) {
+ std::set<int> data;
+ for (auto _ : state) {
+ state.PauseTiming();
+ data = ConstructRandomSet(state.range(0));
+ state.ResumeTiming();
+ for (int j = 0; j < state.range(1); ++j)
+ data.insert(RandomNumber());
+ }
+}
+BENCHMARK(BM_SetInsert)
+ ->Args({1<<10, 128})
+ ->Args({2<<10, 128})
+ ->Args({4<<10, 128})
+ ->Args({8<<10, 128})
+ ->Args({1<<10, 512})
+ ->Args({2<<10, 512})
+ ->Args({4<<10, 512})
+ ->Args({8<<10, 512});
+```
+
+The preceding code is quite repetitive, and can be replaced with the following
+short-hand. The following macro will pick a few appropriate arguments in the
+product of the two specified ranges and will generate a benchmark for each such
+pair.
+
+```c++
+BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {128, 512}});
+```
+
+Some benchmarks may require specific argument values that cannot be expressed
+with `Ranges`. In this case, `ArgsProduct` offers the ability to generate a
+benchmark input for each combination in the product of the supplied vectors.
+
+```c++
+BENCHMARK(BM_SetInsert)
+ ->ArgsProduct({{1<<10, 3<<10, 8<<10}, {20, 40, 60, 80}})
+// would generate the same benchmark arguments as
+BENCHMARK(BM_SetInsert)
+ ->Args({1<<10, 20})
+ ->Args({3<<10, 20})
+ ->Args({8<<10, 20})
+ ->Args({3<<10, 40})
+ ->Args({8<<10, 40})
+ ->Args({1<<10, 40})
+ ->Args({1<<10, 60})
+ ->Args({3<<10, 60})
+ ->Args({8<<10, 60})
+ ->Args({1<<10, 80})
+ ->Args({3<<10, 80})
+ ->Args({8<<10, 80});
+```
+
+For more complex patterns of inputs, passing a custom function to `Apply` allows
+programmatic specification of an arbitrary set of arguments on which to run the
+benchmark. The following example enumerates a dense range on one parameter,
+and a sparse range on the second.
+
+```c++
+static void CustomArguments(benchmark::internal::Benchmark* b) {
+ for (int i = 0; i <= 10; ++i)
+ for (int j = 32; j <= 1024*1024; j *= 8)
+ b->Args({i, j});
+}
+BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
+```
+
+#### Passing Arbitrary Arguments to a Benchmark
+
+In C++11 it is possible to define a benchmark that takes an arbitrary number
+of extra arguments. The `BENCHMARK_CAPTURE(func, test_case_name, ...args)`
+macro creates a benchmark that invokes `func` with the `benchmark::State` as
+the first argument followed by the specified `args...`.
+The `test_case_name` is appended to the name of the benchmark and
+should describe the values passed.
+
+```c++
+template <class ...ExtraArgs>
+void BM_takes_args(benchmark::State& state, ExtraArgs&&... extra_args) {
+ [...]
+}
+// Registers a benchmark named "BM_takes_args/int_string_test" that passes
+// the specified values to `extra_args`.
+BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));
+```
+
+Note that elements of `...args` may refer to global variables. Users should
+avoid modifying global state inside of a benchmark.
+
+<a name="asymptotic-complexity" />
+
+### Calculating Asymptotic Complexity (Big O)
+
+Asymptotic complexity might be calculated for a family of benchmarks. The
+following code will calculate the coefficient for the high-order term in the
+running time and the normalized root-mean square error of string comparison.
+
+```c++
+static void BM_StringCompare(benchmark::State& state) {
+ std::string s1(state.range(0), '-');
+ std::string s2(state.range(0), '-');
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(s1.compare(s2));
+ }
+ state.SetComplexityN(state.range(0));
+}
+BENCHMARK(BM_StringCompare)
+ ->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity(benchmark::oN);
+```
+
+As shown in the following invocation, asymptotic complexity might also be
+calculated automatically.
+
+```c++
+BENCHMARK(BM_StringCompare)
+ ->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity();
+```
+
+The following code will specify asymptotic complexity with a lambda function,
+that might be used to customize high-order term calculation.
+
+```c++
+BENCHMARK(BM_StringCompare)->RangeMultiplier(2)
+ ->Range(1<<10, 1<<18)->Complexity([](benchmark::IterationCount n)->double{return n; });
+```
+
+<a name="custom-benchmark-name" />
+
+### Custom Benchmark Name
+
+You can change the benchmark's name as follows:
+
+```c++
+BENCHMARK(BM_memcpy)->Name("memcpy")->RangeMultiplier(2)->Range(8, 8<<10);
+```
+
+The invocation will execute the benchmark as before using `BM_memcpy` but changes
+the prefix in the report to `memcpy`.
+
+<a name="templated-benchmarks" />
+
+### Templated Benchmarks
+
+This example produces and consumes messages of size `sizeof(v)` `range_x`
+times. It also outputs throughput in the absence of multiprogramming.
+
+```c++
+template <class Q> void BM_Sequential(benchmark::State& state) {
+ Q q;
+ typename Q::value_type v;
+ for (auto _ : state) {
+ for (int i = state.range(0); i--; )
+ q.push(v);
+ for (int e = state.range(0); e--; )
+ q.Wait(&v);
+ }
+ // actually messages, not bytes:
+ state.SetBytesProcessed(
+ static_cast<int64_t>(state.iterations())*state.range(0));
+}
+BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
+```
+
+Three macros are provided for adding benchmark templates.
+
+```c++
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_TEMPLATE(func, ...) // Takes any number of parameters.
+#else // C++ < C++11
+#define BENCHMARK_TEMPLATE(func, arg1)
+#endif
+#define BENCHMARK_TEMPLATE1(func, arg1)
+#define BENCHMARK_TEMPLATE2(func, arg1, arg2)
+```
+
+<a name="fixtures" />
+
+### Fixtures
+
+Fixture tests are created by first defining a type that derives from
+`::benchmark::Fixture` and then creating/registering the tests using the
+following macros:
+
+* `BENCHMARK_F(ClassName, Method)`
+* `BENCHMARK_DEFINE_F(ClassName, Method)`
+* `BENCHMARK_REGISTER_F(ClassName, Method)`
+
+For Example:
+
+```c++
+class MyFixture : public benchmark::Fixture {
+public:
+ void SetUp(const ::benchmark::State& state) {
+ }
+
+ void TearDown(const ::benchmark::State& state) {
+ }
+};
+
+BENCHMARK_F(MyFixture, FooTest)(benchmark::State& st) {
+ for (auto _ : st) {
+ ...
+ }
+}
+
+BENCHMARK_DEFINE_F(MyFixture, BarTest)(benchmark::State& st) {
+ for (auto _ : st) {
+ ...
+ }
+}
+/* BarTest is NOT registered */
+BENCHMARK_REGISTER_F(MyFixture, BarTest)->Threads(2);
+/* BarTest is now registered */
+```
+
+#### Templated Fixtures
+
+Also you can create templated fixture by using the following macros:
+
+* `BENCHMARK_TEMPLATE_F(ClassName, Method, ...)`
+* `BENCHMARK_TEMPLATE_DEFINE_F(ClassName, Method, ...)`
+
+For example:
+
+```c++
+template<typename T>
+class MyFixture : public benchmark::Fixture {};
+
+BENCHMARK_TEMPLATE_F(MyFixture, IntTest, int)(benchmark::State& st) {
+ for (auto _ : st) {
+ ...
+ }
+}
+
+BENCHMARK_TEMPLATE_DEFINE_F(MyFixture, DoubleTest, double)(benchmark::State& st) {
+ for (auto _ : st) {
+ ...
+ }
+}
+
+BENCHMARK_REGISTER_F(MyFixture, DoubleTest)->Threads(2);
+```
+
+<a name="custom-counters" />
+
+### Custom Counters
+
+You can add your own counters with user-defined names. The example below
+will add columns "Foo", "Bar" and "Baz" in its output:
+
+```c++
+static void UserCountersExample1(benchmark::State& state) {
+ double numFoos = 0, numBars = 0, numBazs = 0;
+ for (auto _ : state) {
+ // ... count Foo,Bar,Baz events
+ }
+ state.counters["Foo"] = numFoos;
+ state.counters["Bar"] = numBars;
+ state.counters["Baz"] = numBazs;
+}
+```
+
+The `state.counters` object is a `std::map` with `std::string` keys
+and `Counter` values. The latter is a `double`-like class, via an implicit
+conversion to `double&`. Thus you can use all of the standard arithmetic
+assignment operators (`=,+=,-=,*=,/=`) to change the value of each counter.
+
+In multithreaded benchmarks, each counter is set on the calling thread only.
+When the benchmark finishes, the counters from each thread will be summed;
+the resulting sum is the value which will be shown for the benchmark.
+
+The `Counter` constructor accepts three parameters: the value as a `double`
+; a bit flag which allows you to show counters as rates, and/or as per-thread
+iteration, and/or as per-thread averages, and/or iteration invariants,
+and/or finally inverting the result; and a flag specifying the 'unit' - i.e.
+is 1k a 1000 (default, `benchmark::Counter::OneK::kIs1000`), or 1024
+(`benchmark::Counter::OneK::kIs1024`)?
+
+```c++
+ // sets a simple counter
+ state.counters["Foo"] = numFoos;
+
+ // Set the counter as a rate. It will be presented divided
+ // by the duration of the benchmark.
+ // Meaning: per one second, how many 'foo's are processed?
+ state.counters["FooRate"] = Counter(numFoos, benchmark::Counter::kIsRate);
+
+ // Set the counter as a rate. It will be presented divided
+ // by the duration of the benchmark, and the result inverted.
+ // Meaning: how many seconds it takes to process one 'foo'?
+ state.counters["FooInvRate"] = Counter(numFoos, benchmark::Counter::kIsRate | benchmark::Counter::kInvert);
+
+ // Set the counter as a thread-average quantity. It will
+ // be presented divided by the number of threads.
+ state.counters["FooAvg"] = Counter(numFoos, benchmark::Counter::kAvgThreads);
+
+ // There's also a combined flag:
+ state.counters["FooAvgRate"] = Counter(numFoos,benchmark::Counter::kAvgThreadsRate);
+
+ // This says that we process with the rate of state.range(0) bytes every iteration:
+ state.counters["BytesProcessed"] = Counter(state.range(0), benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::OneK::kIs1024);
+```
+
+When you're compiling in C++11 mode or later you can use `insert()` with
+`std::initializer_list`:
+
+```c++
+ // With C++11, this can be done:
+ state.counters.insert({{"Foo", numFoos}, {"Bar", numBars}, {"Baz", numBazs}});
+ // ... instead of:
+ state.counters["Foo"] = numFoos;
+ state.counters["Bar"] = numBars;
+ state.counters["Baz"] = numBazs;
+```
+
+#### Counter Reporting
+
+When using the console reporter, by default, user counters are printed at
+the end after the table, the same way as ``bytes_processed`` and
+``items_processed``. This is best for cases in which there are few counters,
+or where there are only a couple of lines per benchmark. Here's an example of
+the default output:
+
+```
+------------------------------------------------------------------------------
+Benchmark Time CPU Iterations UserCounters...
+------------------------------------------------------------------------------
+BM_UserCounter/threads:8 2248 ns 10277 ns 68808 Bar=16 Bat=40 Baz=24 Foo=8
+BM_UserCounter/threads:1 9797 ns 9788 ns 71523 Bar=2 Bat=5 Baz=3 Foo=1024m
+BM_UserCounter/threads:2 4924 ns 9842 ns 71036 Bar=4 Bat=10 Baz=6 Foo=2
+BM_UserCounter/threads:4 2589 ns 10284 ns 68012 Bar=8 Bat=20 Baz=12 Foo=4
+BM_UserCounter/threads:8 2212 ns 10287 ns 68040 Bar=16 Bat=40 Baz=24 Foo=8
+BM_UserCounter/threads:16 1782 ns 10278 ns 68144 Bar=32 Bat=80 Baz=48 Foo=16
+BM_UserCounter/threads:32 1291 ns 10296 ns 68256 Bar=64 Bat=160 Baz=96 Foo=32
+BM_UserCounter/threads:4 2615 ns 10307 ns 68040 Bar=8 Bat=20 Baz=12 Foo=4
+BM_Factorial 26 ns 26 ns 26608979 40320
+BM_Factorial/real_time 26 ns 26 ns 26587936 40320
+BM_CalculatePiRange/1 16 ns 16 ns 45704255 0
+BM_CalculatePiRange/8 73 ns 73 ns 9520927 3.28374
+BM_CalculatePiRange/64 609 ns 609 ns 1140647 3.15746
+BM_CalculatePiRange/512 4900 ns 4901 ns 142696 3.14355
+```
+
+If this doesn't suit you, you can print each counter as a table column by
+passing the flag `--benchmark_counters_tabular=true` to the benchmark
+application. This is best for cases in which there are a lot of counters, or
+a lot of lines per individual benchmark. Note that this will trigger a
+reprinting of the table header any time the counter set changes between
+individual benchmarks. Here's an example of corresponding output when
+`--benchmark_counters_tabular=true` is passed:
+
+```
+---------------------------------------------------------------------------------------
+Benchmark Time CPU Iterations Bar Bat Baz Foo
+---------------------------------------------------------------------------------------
+BM_UserCounter/threads:8 2198 ns 9953 ns 70688 16 40 24 8
+BM_UserCounter/threads:1 9504 ns 9504 ns 73787 2 5 3 1
+BM_UserCounter/threads:2 4775 ns 9550 ns 72606 4 10 6 2
+BM_UserCounter/threads:4 2508 ns 9951 ns 70332 8 20 12 4
+BM_UserCounter/threads:8 2055 ns 9933 ns 70344 16 40 24 8
+BM_UserCounter/threads:16 1610 ns 9946 ns 70720 32 80 48 16
+BM_UserCounter/threads:32 1192 ns 9948 ns 70496 64 160 96 32
+BM_UserCounter/threads:4 2506 ns 9949 ns 70332 8 20 12 4
+--------------------------------------------------------------
+Benchmark Time CPU Iterations
+--------------------------------------------------------------
+BM_Factorial 26 ns 26 ns 26392245 40320
+BM_Factorial/real_time 26 ns 26 ns 26494107 40320
+BM_CalculatePiRange/1 15 ns 15 ns 45571597 0
+BM_CalculatePiRange/8 74 ns 74 ns 9450212 3.28374
+BM_CalculatePiRange/64 595 ns 595 ns 1173901 3.15746
+BM_CalculatePiRange/512 4752 ns 4752 ns 147380 3.14355
+BM_CalculatePiRange/4k 37970 ns 37972 ns 18453 3.14184
+BM_CalculatePiRange/32k 303733 ns 303744 ns 2305 3.14162
+BM_CalculatePiRange/256k 2434095 ns 2434186 ns 288 3.1416
+BM_CalculatePiRange/1024k 9721140 ns 9721413 ns 71 3.14159
+BM_CalculatePi/threads:8 2255 ns 9943 ns 70936
+```
+
+Note above the additional header printed when the benchmark changes from
+``BM_UserCounter`` to ``BM_Factorial``. This is because ``BM_Factorial`` does
+not have the same counter set as ``BM_UserCounter``.
+
+<a name="multithreaded-benchmarks"/>
+
+### Multithreaded Benchmarks
+
+In a multithreaded test (benchmark invoked by multiple threads simultaneously),
+it is guaranteed that none of the threads will start until all have reached
+the start of the benchmark loop, and all will have finished before any thread
+exits the benchmark loop. (This behavior is also provided by the `KeepRunning()`
+API) As such, any global setup or teardown can be wrapped in a check against the thread
+index:
+
+```c++
+static void BM_MultiThreaded(benchmark::State& state) {
+ if (state.thread_index == 0) {
+ // Setup code here.
+ }
+ for (auto _ : state) {
+ // Run the test as normal.
+ }
+ if (state.thread_index == 0) {
+ // Teardown code here.
+ }
+}
+BENCHMARK(BM_MultiThreaded)->Threads(2);
+```
+
+If the benchmarked code itself uses threads and you want to compare it to
+single-threaded code, you may want to use real-time ("wallclock") measurements
+for latency comparisons:
+
+```c++
+BENCHMARK(BM_test)->Range(8, 8<<10)->UseRealTime();
+```
+
+Without `UseRealTime`, CPU time is used by default.
+
+<a name="cpu-timers" />
+
+### CPU Timers
+
+By default, the CPU timer only measures the time spent by the main thread.
+If the benchmark itself uses threads internally, this measurement may not
+be what you are looking for. Instead, there is a way to measure the total
+CPU usage of the process, by all the threads.
+
+```c++
+void callee(int i);
+
+static void MyMain(int size) {
+#pragma omp parallel for
+ for(int i = 0; i < size; i++)
+ callee(i);
+}
+
+static void BM_OpenMP(benchmark::State& state) {
+ for (auto _ : state)
+ MyMain(state.range(0));
+}
+
+// Measure the time spent by the main thread, use it to decide for how long to
+// run the benchmark loop. Depending on the internal implementation detail may
+// measure to anywhere from near-zero (the overhead spent before/after work
+// handoff to worker thread[s]) to the whole single-thread time.
+BENCHMARK(BM_OpenMP)->Range(8, 8<<10);
+
+// Measure the user-visible time, the wall clock (literally, the time that
+// has passed on the clock on the wall), use it to decide for how long to
+// run the benchmark loop. This will always be meaningful, an will match the
+// time spent by the main thread in single-threaded case, in general decreasing
+// with the number of internal threads doing the work.
+BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->UseRealTime();
+
+// Measure the total CPU consumption, use it to decide for how long to
+// run the benchmark loop. This will always measure to no less than the
+// time spent by the main thread in single-threaded case.
+BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->MeasureProcessCPUTime();
+
+// A mixture of the last two. Measure the total CPU consumption, but use the
+// wall clock to decide for how long to run the benchmark loop.
+BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->MeasureProcessCPUTime()->UseRealTime();
+```
+
+#### Controlling Timers
+
+Normally, the entire duration of the work loop (`for (auto _ : state) {}`)
+is measured. But sometimes, it is necessary to do some work inside of
+that loop, every iteration, but without counting that time to the benchmark time.
+That is possible, although it is not recommended, since it has high overhead.
+
+```c++
+static void BM_SetInsert_With_Timer_Control(benchmark::State& state) {
+ std::set<int> data;
+ for (auto _ : state) {
+ state.PauseTiming(); // Stop timers. They will not count until they are resumed.
+ data = ConstructRandomSet(state.range(0)); // Do something that should not be measured
+ state.ResumeTiming(); // And resume timers. They are now counting again.
+ // The rest will be measured.
+ for (int j = 0; j < state.range(1); ++j)
+ data.insert(RandomNumber());
+ }
+}
+BENCHMARK(BM_SetInsert_With_Timer_Control)->Ranges({{1<<10, 8<<10}, {128, 512}});
+```
+
+<a name="manual-timing" />
+
+### Manual Timing
+
+For benchmarking something for which neither CPU time nor real-time are
+correct or accurate enough, completely manual timing is supported using
+the `UseManualTime` function.
+
+When `UseManualTime` is used, the benchmarked code must call
+`SetIterationTime` once per iteration of the benchmark loop to
+report the manually measured time.
+
+An example use case for this is benchmarking GPU execution (e.g. OpenCL
+or CUDA kernels, OpenGL or Vulkan or Direct3D draw calls), which cannot
+be accurately measured using CPU time or real-time. Instead, they can be
+measured accurately using a dedicated API, and these measurement results
+can be reported back with `SetIterationTime`.
+
+```c++
+static void BM_ManualTiming(benchmark::State& state) {
+ int microseconds = state.range(0);
+ std::chrono::duration<double, std::micro> sleep_duration {
+ static_cast<double>(microseconds)
+ };
+
+ for (auto _ : state) {
+ auto start = std::chrono::high_resolution_clock::now();
+ // Simulate some useful workload with a sleep
+ std::this_thread::sleep_for(sleep_duration);
+ auto end = std::chrono::high_resolution_clock::now();
+
+ auto elapsed_seconds =
+ std::chrono::duration_cast<std::chrono::duration<double>>(
+ end - start);
+
+ state.SetIterationTime(elapsed_seconds.count());
+ }
+}
+BENCHMARK(BM_ManualTiming)->Range(1, 1<<17)->UseManualTime();
+```
+
+<a name="setting-the-time-unit" />
+
+### Setting the Time Unit
+
+If a benchmark runs a few milliseconds it may be hard to visually compare the
+measured times, since the output data is given in nanoseconds per default. In
+order to manually set the time unit, you can specify it manually:
+
+```c++
+BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
+```
+
+<a name="preventing-optimization" />
+
+### Preventing Optimization
+
+To prevent a value or expression from being optimized away by the compiler
+the `benchmark::DoNotOptimize(...)` and `benchmark::ClobberMemory()`
+functions can be used.
+
+```c++
+static void BM_test(benchmark::State& state) {
+ for (auto _ : state) {
+ int x = 0;
+ for (int i=0; i < 64; ++i) {
+ benchmark::DoNotOptimize(x += i);
+ }
+ }
+}
+```
+
+`DoNotOptimize(<expr>)` forces the *result* of `<expr>` to be stored in either
+memory or a register. For GNU based compilers it acts as read/write barrier
+for global memory. More specifically it forces the compiler to flush pending
+writes to memory and reload any other values as necessary.
+
+Note that `DoNotOptimize(<expr>)` does not prevent optimizations on `<expr>`
+in any way. `<expr>` may even be removed entirely when the result is already
+known. For example:
+
+```c++
+ /* Example 1: `<expr>` is removed entirely. */
+ int foo(int x) { return x + 42; }
+ while (...) DoNotOptimize(foo(0)); // Optimized to DoNotOptimize(42);
+
+ /* Example 2: Result of '<expr>' is only reused */
+ int bar(int) __attribute__((const));
+ while (...) DoNotOptimize(bar(0)); // Optimized to:
+ // int __result__ = bar(0);
+ // while (...) DoNotOptimize(__result__);
+```
+
+The second tool for preventing optimizations is `ClobberMemory()`. In essence
+`ClobberMemory()` forces the compiler to perform all pending writes to global
+memory. Memory managed by block scope objects must be "escaped" using
+`DoNotOptimize(...)` before it can be clobbered. In the below example
+`ClobberMemory()` prevents the call to `v.push_back(42)` from being optimized
+away.
+
+```c++
+static void BM_vector_push_back(benchmark::State& state) {
+ for (auto _ : state) {
+ std::vector<int> v;
+ v.reserve(1);
+ benchmark::DoNotOptimize(v.data()); // Allow v.data() to be clobbered.
+ v.push_back(42);
+ benchmark::ClobberMemory(); // Force 42 to be written to memory.
+ }
+}
+```
+
+Note that `ClobberMemory()` is only available for GNU or MSVC based compilers.
+
+<a name="reporting-statistics" />
+
+### Statistics: Reporting the Mean, Median and Standard Deviation of Repeated Benchmarks
+
+By default each benchmark is run once and that single result is reported.
+However benchmarks are often noisy and a single result may not be representative
+of the overall behavior. For this reason it's possible to repeatedly rerun the
+benchmark.
+
+The number of runs of each benchmark is specified globally by the
+`--benchmark_repetitions` flag or on a per benchmark basis by calling
+`Repetitions` on the registered benchmark object. When a benchmark is run more
+than once the mean, median and standard deviation of the runs will be reported.
+
+Additionally the `--benchmark_report_aggregates_only={true|false}`,
+`--benchmark_display_aggregates_only={true|false}` flags or
+`ReportAggregatesOnly(bool)`, `DisplayAggregatesOnly(bool)` functions can be
+used to change how repeated tests are reported. By default the result of each
+repeated run is reported. When `report aggregates only` option is `true`,
+only the aggregates (i.e. mean, median and standard deviation, maybe complexity
+measurements if they were requested) of the runs is reported, to both the
+reporters - standard output (console), and the file.
+However when only the `display aggregates only` option is `true`,
+only the aggregates are displayed in the standard output, while the file
+output still contains everything.
+Calling `ReportAggregatesOnly(bool)` / `DisplayAggregatesOnly(bool)` on a
+registered benchmark object overrides the value of the appropriate flag for that
+benchmark.
+
+<a name="custom-statistics" />
+
+### Custom Statistics
+
+While having mean, median and standard deviation is nice, this may not be
+enough for everyone. For example you may want to know what the largest
+observation is, e.g. because you have some real-time constraints. This is easy.
+The following code will specify a custom statistic to be calculated, defined
+by a lambda function.
+
+```c++
+void BM_spin_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ for (int x = 0; x < state.range(0); ++x) {
+ benchmark::DoNotOptimize(x);
+ }
+ }
+}
+
+BENCHMARK(BM_spin_empty)
+ ->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
+ return *(std::max_element(std::begin(v), std::end(v)));
+ })
+ ->Arg(512);
+```
+
+<a name="using-register-benchmark" />
+
+### Using RegisterBenchmark(name, fn, args...)
+
+The `RegisterBenchmark(name, func, args...)` function provides an alternative
+way to create and register benchmarks.
+`RegisterBenchmark(name, func, args...)` creates, registers, and returns a
+pointer to a new benchmark with the specified `name` that invokes
+`func(st, args...)` where `st` is a `benchmark::State` object.
+
+Unlike the `BENCHMARK` registration macros, which can only be used at the global
+scope, the `RegisterBenchmark` can be called anywhere. This allows for
+benchmark tests to be registered programmatically.
+
+Additionally `RegisterBenchmark` allows any callable object to be registered
+as a benchmark. Including capturing lambdas and function objects.
+
+For Example:
+```c++
+auto BM_test = [](benchmark::State& st, auto Inputs) { /* ... */ };
+
+int main(int argc, char** argv) {
+ for (auto& test_input : { /* ... */ })
+ benchmark::RegisterBenchmark(test_input.name(), BM_test, test_input);
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+}
+```
+
+<a name="exiting-with-an-error" />
+
+### Exiting with an Error
+
+When errors caused by external influences, such as file I/O and network
+communication, occur within a benchmark the
+`State::SkipWithError(const char* msg)` function can be used to skip that run
+of benchmark and report the error. Note that only future iterations of the
+`KeepRunning()` are skipped. For the ranged-for version of the benchmark loop
+Users must explicitly exit the loop, otherwise all iterations will be performed.
+Users may explicitly return to exit the benchmark immediately.
+
+The `SkipWithError(...)` function may be used at any point within the benchmark,
+including before and after the benchmark loop. Moreover, if `SkipWithError(...)`
+has been used, it is not required to reach the benchmark loop and one may return
+from the benchmark function early.
+
+For example:
+
+```c++
+static void BM_test(benchmark::State& state) {
+ auto resource = GetResource();
+ if (!resource.good()) {
+ state.SkipWithError("Resource is not good!");
+ // KeepRunning() loop will not be entered.
+ }
+ while (state.KeepRunning()) {
+ auto data = resource.read_data();
+ if (!resource.good()) {
+ state.SkipWithError("Failed to read data!");
+ break; // Needed to skip the rest of the iteration.
+ }
+ do_stuff(data);
+ }
+}
+
+static void BM_test_ranged_fo(benchmark::State & state) {
+ auto resource = GetResource();
+ if (!resource.good()) {
+ state.SkipWithError("Resource is not good!");
+ return; // Early return is allowed when SkipWithError() has been used.
+ }
+ for (auto _ : state) {
+ auto data = resource.read_data();
+ if (!resource.good()) {
+ state.SkipWithError("Failed to read data!");
+ break; // REQUIRED to prevent all further iterations.
+ }
+ do_stuff(data);
+ }
+}
+```
+<a name="a-faster-keep-running-loop" />
+
+### A Faster KeepRunning Loop
+
+In C++11 mode, a ranged-based for loop should be used in preference to
+the `KeepRunning` loop for running the benchmarks. For example:
+
+```c++
+static void BM_Fast(benchmark::State &state) {
+ for (auto _ : state) {
+ FastOperation();
+ }
+}
+BENCHMARK(BM_Fast);
+```
+
+The reason the ranged-for loop is faster than using `KeepRunning`, is
+because `KeepRunning` requires a memory load and store of the iteration count
+ever iteration, whereas the ranged-for variant is able to keep the iteration count
+in a register.
+
+For example, an empty inner loop of using the ranged-based for method looks like:
+
+```asm
+# Loop Init
+ mov rbx, qword ptr [r14 + 104]
+ call benchmark::State::StartKeepRunning()
+ test rbx, rbx
+ je .LoopEnd
+.LoopHeader: # =>This Inner Loop Header: Depth=1
+ add rbx, -1
+ jne .LoopHeader
+.LoopEnd:
+```
+
+Compared to an empty `KeepRunning` loop, which looks like:
+
+```asm
+.LoopHeader: # in Loop: Header=BB0_3 Depth=1
+ cmp byte ptr [rbx], 1
+ jne .LoopInit
+.LoopBody: # =>This Inner Loop Header: Depth=1
+ mov rax, qword ptr [rbx + 8]
+ lea rcx, [rax + 1]
+ mov qword ptr [rbx + 8], rcx
+ cmp rax, qword ptr [rbx + 104]
+ jb .LoopHeader
+ jmp .LoopEnd
+.LoopInit:
+ mov rdi, rbx
+ call benchmark::State::StartKeepRunning()
+ jmp .LoopBody
+.LoopEnd:
+```
+
+Unless C++03 compatibility is required, the ranged-for variant of writing
+the benchmark loop should be preferred.
+
+<a name="disabling-cpu-frequency-scaling" />
+
+### Disabling CPU Frequency Scaling
+
+If you see this error:
+
+```
+***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
+```
+
+you might want to disable the CPU frequency scaling while running the benchmark:
+
+```bash
+sudo cpupower frequency-set --governor performance
+./mybench
+sudo cpupower frequency-set --governor powersave
+```
--- /dev/null
+workspace(name = "com_github_google_benchmark")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "rules_cc",
+ strip_prefix = "rules_cc-a508235df92e71d537fcbae0c7c952ea6957a912",
+ urls = ["https://github.com/bazelbuild/rules_cc/archive/a508235df92e71d537fcbae0c7c952ea6957a912.zip"],
+ sha256 = "d7dc12c1d5bc1a87474de8e3d17b7731a4dcebcfb8aa3990fe8ac7734ef12f2f",
+)
+
+http_archive(
+ name = "com_google_absl",
+ sha256 = "f41868f7a938605c92936230081175d1eae87f6ea2c248f41077c8f88316f111",
+ strip_prefix = "abseil-cpp-20200225.2",
+ urls = ["https://github.com/abseil/abseil-cpp/archive/20200225.2.tar.gz"],
+)
+
+http_archive(
+ name = "com_google_googletest",
+ strip_prefix = "googletest-3f0cf6b62ad1eb50d8736538363d3580dd640c3e",
+ urls = ["https://github.com/google/googletest/archive/3f0cf6b62ad1eb50d8736538363d3580dd640c3e.zip"],
+ sha256 = "8f827dd550db8b4fdf73904690df0be9fccc161017c9038a724bc9a0617a1bc8",
+)
+
+http_archive(
+ name = "pybind11",
+ build_file = "@//bindings/python:pybind11.BUILD",
+ sha256 = "1eed57bc6863190e35637290f97a20c81cfe4d9090ac0a24f3bbf08f265eb71d",
+ strip_prefix = "pybind11-2.4.3",
+ urls = ["https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz"],
+)
+
+new_local_repository(
+ name = "python_headers",
+ build_file = "@//bindings/python:python_headers.BUILD",
+ path = "/usr/include/python3.6", # May be overwritten by setup.py.
+)
+
+http_archive(
+ name = "rules_python",
+ url = "https://github.com/bazelbuild/rules_python/releases/download/0.1.0/rules_python-0.1.0.tar.gz",
+ sha256 = "b6d46438523a3ec0f3cead544190ee13223a52f6a6765a29eae7b7cc24cc83a0",
+)
+
+load("@rules_python//python:pip.bzl", pip3_install="pip_install")
+
+pip3_install(
+ name = "py_deps",
+ requirements = "//:requirements.txt",
+)
--- /dev/null
+theme: jekyll-theme-midnight
+markdown: GFM
--- /dev/null
+version: '{build}'
+
+image: Visual Studio 2017
+
+configuration:
+ - Debug
+ - Release
+
+environment:
+ matrix:
+ - compiler: msvc-15-seh
+ generator: "Visual Studio 15 2017"
+
+ - compiler: msvc-15-seh
+ generator: "Visual Studio 15 2017 Win64"
+
+ - compiler: msvc-14-seh
+ generator: "Visual Studio 14 2015"
+
+ - compiler: msvc-14-seh
+ generator: "Visual Studio 14 2015 Win64"
+
+ - compiler: gcc-5.3.0-posix
+ generator: "MinGW Makefiles"
+ cxx_path: 'C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin'
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+
+matrix:
+ fast_finish: true
+
+install:
+ # git bash conflicts with MinGW makefiles
+ - if "%generator%"=="MinGW Makefiles" (set "PATH=%PATH:C:\Program Files\Git\usr\bin;=%")
+ - if not "%cxx_path%"=="" (set "PATH=%PATH%;%cxx_path%")
+
+build_script:
+ - md _build -Force
+ - cd _build
+ - echo %configuration%
+ - cmake -G "%generator%" "-DCMAKE_BUILD_TYPE=%configuration%" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON ..
+ - cmake --build . --config %configuration%
+
+test_script:
+ - ctest --build-config %configuration% --timeout 300 --output-on-failure
+
+artifacts:
+ - path: '_build/CMakeFiles/*.log'
+ name: logs
+ - path: '_build/Testing/**/*.xml'
+ name: test_results
--- /dev/null
+exports_files(glob(["*.BUILD"]))
+exports_files(["build_defs.bzl"])
+
--- /dev/null
+_SHARED_LIB_SUFFIX = {
+ "//conditions:default": ".so",
+ "//:windows": ".dll",
+}
+
+def py_extension(name, srcs, hdrs = [], copts = [], features = [], deps = []):
+ for shared_lib_suffix in _SHARED_LIB_SUFFIX.values():
+ shared_lib_name = name + shared_lib_suffix
+ native.cc_binary(
+ name = shared_lib_name,
+ linkshared = 1,
+ linkstatic = 1,
+ srcs = srcs + hdrs,
+ copts = copts,
+ features = features,
+ deps = deps,
+ )
+
+ return native.py_library(
+ name = name,
+ data = select({
+ platform: [name + shared_lib_suffix]
+ for platform, shared_lib_suffix in _SHARED_LIB_SUFFIX.items()
+ }),
+ )
--- /dev/null
+load("//bindings/python:build_defs.bzl", "py_extension")
+
+py_library(
+ name = "google_benchmark",
+ srcs = ["__init__.py"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":_benchmark",
+ # pip; absl:app
+ ],
+)
+
+py_extension(
+ name = "_benchmark",
+ srcs = ["benchmark.cc"],
+ copts = [
+ "-fexceptions",
+ "-fno-strict-aliasing",
+ ],
+ features = ["-use_header_modules"],
+ deps = [
+ "//:benchmark",
+ "@pybind11",
+ "@python_headers",
+ ],
+)
+
+py_test(
+ name = "example",
+ srcs = ["example.py"],
+ python_version = "PY3",
+ srcs_version = "PY3",
+ visibility = ["//visibility:public"],
+ deps = [
+ ":google_benchmark",
+ ],
+)
+
--- /dev/null
+# Copyright 2020 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Python benchmarking utilities.
+
+Example usage:
+ import google_benchmark as benchmark
+
+ @benchmark.register
+ def my_benchmark(state):
+ ... # Code executed outside `while` loop is not timed.
+
+ while state:
+ ... # Code executed within `while` loop is timed.
+
+ if __name__ == '__main__':
+ benchmark.main()
+"""
+
+from absl import app
+from google_benchmark import _benchmark
+from google_benchmark._benchmark import (
+ Counter,
+ kNanosecond,
+ kMicrosecond,
+ kMillisecond,
+ kSecond,
+ oNone,
+ o1,
+ oN,
+ oNSquared,
+ oNCubed,
+ oLogN,
+ oNLogN,
+ oAuto,
+ oLambda,
+)
+
+
+__all__ = [
+ "register",
+ "main",
+ "Counter",
+ "kNanosecond",
+ "kMicrosecond",
+ "kMillisecond",
+ "kSecond",
+ "oNone",
+ "o1",
+ "oN",
+ "oNSquared",
+ "oNCubed",
+ "oLogN",
+ "oNLogN",
+ "oAuto",
+ "oLambda",
+]
+
+__version__ = "0.2.0"
+
+
+class __OptionMaker:
+ """A stateless class to collect benchmark options.
+
+ Collect all decorator calls like @option.range(start=0, limit=1<<5).
+ """
+
+ class Options:
+ """Pure data class to store options calls, along with the benchmarked function."""
+
+ def __init__(self, func):
+ self.func = func
+ self.builder_calls = []
+
+ @classmethod
+ def make(cls, func_or_options):
+ """Make Options from Options or the benchmarked function."""
+ if isinstance(func_or_options, cls.Options):
+ return func_or_options
+ return cls.Options(func_or_options)
+
+ def __getattr__(self, builder_name):
+ """Append option call in the Options."""
+
+ # The function that get returned on @option.range(start=0, limit=1<<5).
+ def __builder_method(*args, **kwargs):
+
+ # The decorator that get called, either with the benchmared function
+ # or the previous Options
+ def __decorator(func_or_options):
+ options = self.make(func_or_options)
+ options.builder_calls.append((builder_name, args, kwargs))
+ # The decorator returns Options so it is not technically a decorator
+ # and needs a final call to @regiser
+ return options
+
+ return __decorator
+
+ return __builder_method
+
+
+# Alias for nicer API.
+# We have to instantiate an object, even if stateless, to be able to use __getattr__
+# on option.range
+option = __OptionMaker()
+
+
+def register(undefined=None, *, name=None):
+ """Register function for benchmarking."""
+ if undefined is None:
+ # Decorator is called without parenthesis so we return a decorator
+ return lambda f: register(f, name=name)
+
+ # We have either the function to benchmark (simple case) or an instance of Options
+ # (@option._ case).
+ options = __OptionMaker.make(undefined)
+
+ if name is None:
+ name = options.func.__name__
+
+ # We register the benchmark and reproduce all the @option._ calls onto the
+ # benchmark builder pattern
+ benchmark = _benchmark.RegisterBenchmark(name, options.func)
+ for name, args, kwargs in options.builder_calls[::-1]:
+ getattr(benchmark, name)(*args, **kwargs)
+
+ # return the benchmarked function because the decorator does not modify it
+ return options.func
+
+
+def _flags_parser(argv):
+ argv = _benchmark.Initialize(argv)
+ return app.parse_flags_with_usage(argv)
+
+
+def _run_benchmarks(argv):
+ if len(argv) > 1:
+ raise app.UsageError("Too many command-line arguments.")
+ return _benchmark.RunSpecifiedBenchmarks()
+
+
+def main(argv=None):
+ return app.run(_run_benchmarks, argv=argv, flags_parser=_flags_parser)
+
+
+# Methods for use with custom main function.
+initialize = _benchmark.Initialize
+run_benchmarks = _benchmark.RunSpecifiedBenchmarks
--- /dev/null
+// Benchmark for Python.
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "pybind11/operators.h"
+#include "pybind11/pybind11.h"
+#include "pybind11/stl.h"
+#include "pybind11/stl_bind.h"
+
+#include "benchmark/benchmark.h"
+
+PYBIND11_MAKE_OPAQUE(benchmark::UserCounters);
+
+namespace {
+namespace py = ::pybind11;
+
+std::vector<std::string> Initialize(const std::vector<std::string>& argv) {
+ // The `argv` pointers here become invalid when this function returns, but
+ // benchmark holds the pointer to `argv[0]`. We create a static copy of it
+ // so it persists, and replace the pointer below.
+ static std::string executable_name(argv[0]);
+ std::vector<char*> ptrs;
+ ptrs.reserve(argv.size());
+ for (auto& arg : argv) {
+ ptrs.push_back(const_cast<char*>(arg.c_str()));
+ }
+ ptrs[0] = const_cast<char*>(executable_name.c_str());
+ int argc = static_cast<int>(argv.size());
+ benchmark::Initialize(&argc, ptrs.data());
+ std::vector<std::string> remaining_argv;
+ remaining_argv.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ remaining_argv.emplace_back(ptrs[i]);
+ }
+ return remaining_argv;
+}
+
+benchmark::internal::Benchmark* RegisterBenchmark(const char* name,
+ py::function f) {
+ return benchmark::RegisterBenchmark(
+ name, [f](benchmark::State& state) { f(&state); });
+}
+
+PYBIND11_MODULE(_benchmark, m) {
+ using benchmark::TimeUnit;
+ py::enum_<TimeUnit>(m, "TimeUnit")
+ .value("kNanosecond", TimeUnit::kNanosecond)
+ .value("kMicrosecond", TimeUnit::kMicrosecond)
+ .value("kMillisecond", TimeUnit::kMillisecond)
+ .value("kSecond", TimeUnit::kSecond)
+ .export_values();
+
+ using benchmark::BigO;
+ py::enum_<BigO>(m, "BigO")
+ .value("oNone", BigO::oNone)
+ .value("o1", BigO::o1)
+ .value("oN", BigO::oN)
+ .value("oNSquared", BigO::oNSquared)
+ .value("oNCubed", BigO::oNCubed)
+ .value("oLogN", BigO::oLogN)
+ .value("oNLogN", BigO::oLogN)
+ .value("oAuto", BigO::oAuto)
+ .value("oLambda", BigO::oLambda)
+ .export_values();
+
+ using benchmark::internal::Benchmark;
+ py::class_<Benchmark>(m, "Benchmark")
+ // For methods returning a pointer tor the current object, reference
+ // return policy is used to ask pybind not to take ownership oof the
+ // returned object and avoid calling delete on it.
+ // https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies
+ //
+ // For methods taking a const std::vector<...>&, a copy is created
+ // because a it is bound to a Python list.
+ // https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
+ .def("unit", &Benchmark::Unit, py::return_value_policy::reference)
+ .def("arg", &Benchmark::Arg, py::return_value_policy::reference)
+ .def("args", &Benchmark::Args, py::return_value_policy::reference)
+ .def("range", &Benchmark::Range, py::return_value_policy::reference,
+ py::arg("start"), py::arg("limit"))
+ .def("dense_range", &Benchmark::DenseRange,
+ py::return_value_policy::reference, py::arg("start"),
+ py::arg("limit"), py::arg("step") = 1)
+ .def("ranges", &Benchmark::Ranges, py::return_value_policy::reference)
+ .def("args_product", &Benchmark::ArgsProduct,
+ py::return_value_policy::reference)
+ .def("arg_name", &Benchmark::ArgName, py::return_value_policy::reference)
+ .def("arg_names", &Benchmark::ArgNames,
+ py::return_value_policy::reference)
+ .def("range_pair", &Benchmark::RangePair,
+ py::return_value_policy::reference, py::arg("lo1"), py::arg("hi1"),
+ py::arg("lo2"), py::arg("hi2"))
+ .def("range_multiplier", &Benchmark::RangeMultiplier,
+ py::return_value_policy::reference)
+ .def("min_time", &Benchmark::MinTime, py::return_value_policy::reference)
+ .def("iterations", &Benchmark::Iterations,
+ py::return_value_policy::reference)
+ .def("repetitions", &Benchmark::Repetitions,
+ py::return_value_policy::reference)
+ .def("report_aggregates_only", &Benchmark::ReportAggregatesOnly,
+ py::return_value_policy::reference, py::arg("value") = true)
+ .def("display_aggregates_only", &Benchmark::DisplayAggregatesOnly,
+ py::return_value_policy::reference, py::arg("value") = true)
+ .def("measure_process_cpu_time", &Benchmark::MeasureProcessCPUTime,
+ py::return_value_policy::reference)
+ .def("use_real_time", &Benchmark::UseRealTime,
+ py::return_value_policy::reference)
+ .def("use_manual_time", &Benchmark::UseManualTime,
+ py::return_value_policy::reference)
+ .def(
+ "complexity",
+ (Benchmark * (Benchmark::*)(benchmark::BigO)) & Benchmark::Complexity,
+ py::return_value_policy::reference,
+ py::arg("complexity") = benchmark::oAuto);
+
+ using benchmark::Counter;
+ py::class_<Counter> py_counter(m, "Counter");
+
+ py::enum_<Counter::Flags>(py_counter, "Flags")
+ .value("kDefaults", Counter::Flags::kDefaults)
+ .value("kIsRate", Counter::Flags::kIsRate)
+ .value("kAvgThreads", Counter::Flags::kAvgThreads)
+ .value("kAvgThreadsRate", Counter::Flags::kAvgThreadsRate)
+ .value("kIsIterationInvariant", Counter::Flags::kIsIterationInvariant)
+ .value("kIsIterationInvariantRate",
+ Counter::Flags::kIsIterationInvariantRate)
+ .value("kAvgIterations", Counter::Flags::kAvgIterations)
+ .value("kAvgIterationsRate", Counter::Flags::kAvgIterationsRate)
+ .value("kInvert", Counter::Flags::kInvert)
+ .export_values()
+ .def(py::self | py::self);
+
+ py::enum_<Counter::OneK>(py_counter, "OneK")
+ .value("kIs1000", Counter::OneK::kIs1000)
+ .value("kIs1024", Counter::OneK::kIs1024)
+ .export_values();
+
+ py_counter
+ .def(py::init<double, Counter::Flags, Counter::OneK>(),
+ py::arg("value") = 0., py::arg("flags") = Counter::kDefaults,
+ py::arg("k") = Counter::kIs1000)
+ .def(py::init([](double value) { return Counter(value); }))
+ .def_readwrite("value", &Counter::value)
+ .def_readwrite("flags", &Counter::flags)
+ .def_readwrite("oneK", &Counter::oneK);
+ py::implicitly_convertible<py::float_, Counter>();
+ py::implicitly_convertible<py::int_, Counter>();
+
+ py::bind_map<benchmark::UserCounters>(m, "UserCounters");
+
+ using benchmark::State;
+ py::class_<State>(m, "State")
+ .def("__bool__", &State::KeepRunning)
+ .def_property_readonly("keep_running", &State::KeepRunning)
+ .def("pause_timing", &State::PauseTiming)
+ .def("resume_timing", &State::ResumeTiming)
+ .def("skip_with_error", &State::SkipWithError)
+ .def_property_readonly("error_occurred", &State::error_occurred)
+ .def("set_iteration_time", &State::SetIterationTime)
+ .def_property("bytes_processed", &State::bytes_processed,
+ &State::SetBytesProcessed)
+ .def_property("complexity_n", &State::complexity_length_n,
+ &State::SetComplexityN)
+ .def_property("items_processed", &State::items_processed,
+ &State::SetItemsProcessed)
+ .def("set_label", (void (State::*)(const char*)) & State::SetLabel)
+ .def("range", &State::range, py::arg("pos") = 0)
+ .def_property_readonly("iterations", &State::iterations)
+ .def_readwrite("counters", &State::counters)
+ .def_readonly("thread_index", &State::thread_index)
+ .def_readonly("threads", &State::threads);
+
+ m.def("Initialize", Initialize);
+ m.def("RegisterBenchmark", RegisterBenchmark,
+ py::return_value_policy::reference);
+ m.def("RunSpecifiedBenchmarks",
+ []() { benchmark::RunSpecifiedBenchmarks(); });
+};
+} // namespace
--- /dev/null
+# Copyright 2020 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Example of Python using C++ benchmark framework.
+
+To run this example, you must first install the `google_benchmark` Python package.
+
+To install using `setup.py`, download and extract the `google_benchmark` source.
+In the extracted directory, execute:
+ python setup.py install
+"""
+
+import random
+import time
+
+import google_benchmark as benchmark
+from google_benchmark import Counter
+
+
+@benchmark.register
+def empty(state):
+ while state:
+ pass
+
+
+@benchmark.register
+def sum_million(state):
+ while state:
+ sum(range(1_000_000))
+
+@benchmark.register
+def pause_timing(state):
+ """Pause timing every iteration."""
+ while state:
+ # Construct a list of random ints every iteration without timing it
+ state.pause_timing()
+ random_list = [random.randint(0, 100) for _ in range(100)]
+ state.resume_timing()
+ # Time the in place sorting algorithm
+ random_list.sort()
+
+
+@benchmark.register
+def skipped(state):
+ if True: # Test some predicate here.
+ state.skip_with_error("some error")
+ return # NOTE: You must explicitly return, or benchmark will continue.
+
+ ... # Benchmark code would be here.
+
+
+@benchmark.register
+def manual_timing(state):
+ while state:
+ # Manually count Python CPU time
+ start = time.perf_counter() # perf_counter_ns() in Python 3.7+
+ # Something to benchmark
+ time.sleep(0.01)
+ end = time.perf_counter()
+ state.set_iteration_time(end - start)
+
+
+@benchmark.register
+def custom_counters(state):
+ """Collect cutom metric using benchmark.Counter."""
+ num_foo = 0.0
+ while state:
+ # Benchmark some code here
+ pass
+ # Collect some custom metric named foo
+ num_foo += 0.13
+
+ # Automatic Counter from numbers.
+ state.counters["foo"] = num_foo
+ # Set a counter as a rate.
+ state.counters["foo_rate"] = Counter(num_foo, Counter.kIsRate)
+ # Set a counter as an inverse of rate.
+ state.counters["foo_inv_rate"] = Counter(num_foo, Counter.kIsRate | Counter.kInvert)
+ # Set a counter as a thread-average quantity.
+ state.counters["foo_avg"] = Counter(num_foo, Counter.kAvgThreads)
+ # There's also a combined flag:
+ state.counters["foo_avg_rate"] = Counter(num_foo, Counter.kAvgThreadsRate)
+
+
+@benchmark.register
+@benchmark.option.measure_process_cpu_time()
+@benchmark.option.use_real_time()
+def with_options(state):
+ while state:
+ sum(range(1_000_000))
+
+
+@benchmark.register(name="sum_million_microseconds")
+@benchmark.option.unit(benchmark.kMicrosecond)
+def with_options(state):
+ while state:
+ sum(range(1_000_000))
+
+
+@benchmark.register
+@benchmark.option.arg(100)
+@benchmark.option.arg(1000)
+def passing_argument(state):
+ while state:
+ sum(range(state.range(0)))
+
+
+@benchmark.register
+@benchmark.option.range(8, limit=8 << 10)
+def using_range(state):
+ while state:
+ sum(range(state.range(0)))
+
+
+@benchmark.register
+@benchmark.option.range_multiplier(2)
+@benchmark.option.range(1 << 10, 1 << 18)
+@benchmark.option.complexity(benchmark.oN)
+def computing_complexity(state):
+ while state:
+ sum(range(state.range(0)))
+ state.complexity_n = state.range(0)
+
+
+if __name__ == "__main__":
+ benchmark.main()
--- /dev/null
+cc_library(
+ name = "pybind11",
+ hdrs = glob(
+ include = [
+ "include/pybind11/*.h",
+ "include/pybind11/detail/*.h",
+ ],
+ exclude = [
+ "include/pybind11/common.h",
+ "include/pybind11/eigen.h",
+ ],
+ ),
+ copts = [
+ "-fexceptions",
+ "-Wno-undefined-inline",
+ "-Wno-pragma-once-outside-header",
+ ],
+ includes = ["include"],
+ visibility = ["//visibility:public"],
+)
--- /dev/null
+cc_library(
+ name = "python_headers",
+ hdrs = glob(["**/*.h"]),
+ includes = ["."],
+ visibility = ["//visibility:public"],
+)
--- /dev/null
+absl-py>=0.7.1
+
--- /dev/null
+# - Adds a compiler flag if it is supported by the compiler
+#
+# This function checks that the supplied compiler flag is supported and then
+# adds it to the corresponding compiler flags
+#
+# add_cxx_compiler_flag(<FLAG> [<VARIANT>])
+#
+# - Example
+#
+# include(AddCXXCompilerFlag)
+# add_cxx_compiler_flag(-Wall)
+# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)
+# Requires CMake 2.6+
+
+if(__add_cxx_compiler_flag)
+ return()
+endif()
+set(__add_cxx_compiler_flag INCLUDED)
+
+include(CheckCXXCompilerFlag)
+
+function(mangle_compiler_flag FLAG OUTPUT)
+ string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG)
+ string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG})
+ string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
+ string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
+ set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE)
+endfunction(mangle_compiler_flag)
+
+function(add_cxx_compiler_flag FLAG)
+ mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
+ set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
+ check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
+ set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
+ if(${MANGLED_FLAG})
+ if(ARGC GREATER 1)
+ set(VARIANT ${ARGV1})
+ string(TOUPPER "_${VARIANT}" VARIANT)
+ else()
+ set(VARIANT "")
+ endif()
+ set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(add_required_cxx_compiler_flag FLAG)
+ mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
+ set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
+ check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
+ set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
+ if(${MANGLED_FLAG})
+ if(ARGC GREATER 1)
+ set(VARIANT ${ARGV1})
+ string(TOUPPER "_${VARIANT}" VARIANT)
+ else()
+ set(VARIANT "")
+ endif()
+ set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
+ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE)
+ else()
+ message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler")
+ endif()
+endfunction()
+
+function(check_cxx_warning_flag FLAG)
+ mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
+ set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+ # Add -Werror to ensure the compiler generates an error if the warning flag
+ # doesn't exist.
+ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror ${FLAG}")
+ check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
+ set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
+endfunction()
--- /dev/null
+# - Compile and run code to check for C++ features
+#
+# This functions compiles a source file under the `cmake` folder
+# and adds the corresponding `HAVE_[FILENAME]` flag to the CMake
+# environment
+#
+# cxx_feature_check(<FLAG> [<VARIANT>])
+#
+# - Example
+#
+# include(CXXFeatureCheck)
+# cxx_feature_check(STD_REGEX)
+# Requires CMake 2.8.12+
+
+if(__cxx_feature_check)
+ return()
+endif()
+set(__cxx_feature_check INCLUDED)
+
+function(cxx_feature_check FILE)
+ string(TOLOWER ${FILE} FILE)
+ string(TOUPPER ${FILE} VAR)
+ string(TOUPPER "HAVE_${VAR}" FEATURE)
+ if (DEFINED HAVE_${VAR})
+ set(HAVE_${VAR} 1 PARENT_SCOPE)
+ add_definitions(-DHAVE_${VAR})
+ return()
+ endif()
+
+ if (ARGC GREATER 1)
+ message(STATUS "Enabling additional flags: ${ARGV1}")
+ list(APPEND BENCHMARK_CXX_LINKER_FLAGS ${ARGV1})
+ endif()
+
+ if (NOT DEFINED COMPILE_${FEATURE})
+ message(STATUS "Performing Test ${FEATURE}")
+ if(CMAKE_CROSSCOMPILING)
+ try_compile(COMPILE_${FEATURE}
+ ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
+ CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
+ LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
+ if(COMPILE_${FEATURE})
+ message(WARNING
+ "If you see build failures due to cross compilation, try setting HAVE_${VAR} to 0")
+ set(RUN_${FEATURE} 0 CACHE INTERNAL "")
+ else()
+ set(RUN_${FEATURE} 1 CACHE INTERNAL "")
+ endif()
+ else()
+ message(STATUS "Performing Test ${FEATURE}")
+ try_run(RUN_${FEATURE} COMPILE_${FEATURE}
+ ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
+ CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
+ LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
+ endif()
+ endif()
+
+ if(RUN_${FEATURE} EQUAL 0)
+ message(STATUS "Performing Test ${FEATURE} -- success")
+ set(HAVE_${VAR} 1 PARENT_SCOPE)
+ add_definitions(-DHAVE_${VAR})
+ else()
+ if(NOT COMPILE_${FEATURE})
+ message(STATUS "Performing Test ${FEATURE} -- failed to compile")
+ else()
+ message(STATUS "Performing Test ${FEATURE} -- compiled but failed to run")
+ endif()
+ endif()
+endfunction()
--- /dev/null
+include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
--- /dev/null
+# - Returns a version string from Git tags
+#
+# This function inspects the annotated git tags for the project and returns a string
+# into a CMake variable
+#
+# get_git_version(<var>)
+#
+# - Example
+#
+# include(GetGitVersion)
+# get_git_version(GIT_VERSION)
+#
+# Requires CMake 2.8.11+
+find_package(Git)
+
+if(__get_git_version)
+ return()
+endif()
+set(__get_git_version INCLUDED)
+
+function(get_git_version var)
+ if(GIT_EXECUTABLE)
+ execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ RESULT_VARIABLE status
+ OUTPUT_VARIABLE GIT_DESCRIBE_VERSION
+ ERROR_QUIET)
+ if(status)
+ set(GIT_DESCRIBE_VERSION "v0.0.0")
+ endif()
+
+ string(STRIP ${GIT_DESCRIBE_VERSION} GIT_DESCRIBE_VERSION)
+ if(GIT_DESCRIBE_VERSION MATCHES v[^-]*-)
+ string(REGEX REPLACE "v([^-]*)-([0-9]+)-.*" "\\1.\\2" GIT_VERSION ${GIT_DESCRIBE_VERSION})
+ else()
+ string(REGEX REPLACE "v(.*)" "\\1" GIT_VERSION ${GIT_DESCRIBE_VERSION})
+ endif()
+
+ # Work out if the repository is dirty
+ execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ OUTPUT_QUIET
+ ERROR_QUIET)
+ execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD --
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ OUTPUT_VARIABLE GIT_DIFF_INDEX
+ ERROR_QUIET)
+ string(COMPARE NOTEQUAL "${GIT_DIFF_INDEX}" "" GIT_DIRTY)
+ if (${GIT_DIRTY})
+ set(GIT_DESCRIBE_VERSION "${GIT_DESCRIBE_VERSION}-dirty")
+ endif()
+ message(STATUS "git version: ${GIT_DESCRIBE_VERSION} normalized to ${GIT_VERSION}")
+ else()
+ set(GIT_VERSION "0.0.0")
+ endif()
+
+ set(${var} ${GIT_VERSION} PARENT_SCOPE)
+endfunction()
--- /dev/null
+# Download and unpack googletest at configure time
+set(GOOGLETEST_PREFIX "${benchmark_BINARY_DIR}/third_party/googletest")
+configure_file(${benchmark_SOURCE_DIR}/cmake/GoogleTest.cmake.in ${GOOGLETEST_PREFIX}/CMakeLists.txt @ONLY)
+
+set(GOOGLETEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}/googletest" CACHE PATH "") # Mind the quotes
+execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
+ -DALLOW_DOWNLOADING_GOOGLETEST=${BENCHMARK_DOWNLOAD_DEPENDENCIES} -DGOOGLETEST_PATH:PATH=${GOOGLETEST_PATH} .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
+)
+
+if(result)
+ message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+endif()
+
+execute_process(
+ COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
+)
+
+if(result)
+ message(FATAL_ERROR "Build step for googletest failed: ${result}")
+endif()
+
+# Prevent overriding the parent project's compiler/linker
+# settings on Windows
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+include(${GOOGLETEST_PREFIX}/googletest-paths.cmake)
+
+# Add googletest directly to our build. This defines
+# the gtest and gtest_main targets.
+add_subdirectory(${GOOGLETEST_SOURCE_DIR}
+ ${GOOGLETEST_BINARY_DIR}
+ EXCLUDE_FROM_ALL)
+
+set_target_properties(gtest PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest,INTERFACE_INCLUDE_DIRECTORIES>)
+set_target_properties(gtest_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest_main,INTERFACE_INCLUDE_DIRECTORIES>)
+set_target_properties(gmock PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock,INTERFACE_INCLUDE_DIRECTORIES>)
+set_target_properties(gmock_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock_main,INTERFACE_INCLUDE_DIRECTORIES>)
--- /dev/null
+cmake_minimum_required(VERSION 2.8.12)
+
+project(googletest-download NONE)
+
+# Enable ExternalProject CMake module
+include(ExternalProject)
+
+option(ALLOW_DOWNLOADING_GOOGLETEST "If googletest src tree is not found in location specified by GOOGLETEST_PATH, do fetch the archive from internet" OFF)
+set(GOOGLETEST_PATH "/usr/src/googletest" CACHE PATH
+ "Path to the googletest root tree. Should contain googletest and googlemock subdirs. And CMakeLists.txt in root, and in both of these subdirs")
+
+# Download and install GoogleTest
+
+message(STATUS "Looking for Google Test sources")
+message(STATUS "Looking for Google Test sources in ${GOOGLETEST_PATH}")
+if(EXISTS "${GOOGLETEST_PATH}" AND IS_DIRECTORY "${GOOGLETEST_PATH}" AND EXISTS "${GOOGLETEST_PATH}/CMakeLists.txt" AND
+ EXISTS "${GOOGLETEST_PATH}/googletest" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googletest" AND EXISTS "${GOOGLETEST_PATH}/googletest/CMakeLists.txt" AND
+ EXISTS "${GOOGLETEST_PATH}/googlemock" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googlemock" AND EXISTS "${GOOGLETEST_PATH}/googlemock/CMakeLists.txt")
+ message(STATUS "Found Google Test in ${GOOGLETEST_PATH}")
+
+ ExternalProject_Add(
+ googletest
+ PREFIX "${CMAKE_BINARY_DIR}"
+ DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
+ SOURCE_DIR "${GOOGLETEST_PATH}" # use existing src dir.
+ BINARY_DIR "${CMAKE_BINARY_DIR}/build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+ )
+else()
+ if(NOT ALLOW_DOWNLOADING_GOOGLETEST)
+ message(SEND_ERROR "Did not find Google Test sources! Either pass correct path in GOOGLETEST_PATH, or enable BENCHMARK_DOWNLOAD_DEPENDENCIES, or disable BENCHMARK_ENABLE_GTEST_TESTS / BENCHMARK_ENABLE_TESTING.")
+ else()
+ message(WARNING "Did not find Google Test sources! Fetching from web...")
+ ExternalProject_Add(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG master
+ PREFIX "${CMAKE_BINARY_DIR}"
+ STAMP_DIR "${CMAKE_BINARY_DIR}/stamp"
+ DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
+ SOURCE_DIR "${CMAKE_BINARY_DIR}/src"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+ )
+ endif()
+endif()
+
+ExternalProject_Get_Property(googletest SOURCE_DIR BINARY_DIR)
+file(WRITE googletest-paths.cmake
+"set(GOOGLETEST_SOURCE_DIR \"${SOURCE_DIR}\")
+set(GOOGLETEST_BINARY_DIR \"${BINARY_DIR}\")
+")
--- /dev/null
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: @PROJECT_NAME@
+Description: Google microbenchmark framework
+Version: @VERSION@
+
+Libs: -L${libdir} -lbenchmark
+Libs.private: -lpthread
+Cflags: -I${includedir}
--- /dev/null
+#include <gnuregex.h>
+#include <string>
+int main() {
+ std::string str = "test0159";
+ regex_t re;
+ int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB);
+ if (ec != 0) {
+ return ec;
+ }
+ return regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0;
+}
+
--- /dev/null
+find_package(LLVMAr REQUIRED)
+set(CMAKE_AR "${LLVMAR_EXECUTABLE}" CACHE FILEPATH "" FORCE)
+
+find_package(LLVMNm REQUIRED)
+set(CMAKE_NM "${LLVMNM_EXECUTABLE}" CACHE FILEPATH "" FORCE)
+
+find_package(LLVMRanLib REQUIRED)
+set(CMAKE_RANLIB "${LLVMRANLIB_EXECUTABLE}" CACHE FILEPATH "" FORCE)
--- /dev/null
+#include <regex.h>
+#include <string>
+int main() {
+ std::string str = "test0159";
+ regex_t re;
+ int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB);
+ if (ec != 0) {
+ return ec;
+ }
+ int ret = regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0;
+ regfree(&re);
+ return ret;
+}
+
--- /dev/null
+macro(split_list listname)
+ string(REPLACE ";" " " ${listname} "${${listname}}")
+endmacro()
--- /dev/null
+#include <regex>
+#include <string>
+int main() {
+ const std::string str = "test0159";
+ std::regex re;
+ re = std::regex("^[a-z]+[0-9]+$",
+ std::regex_constants::extended | std::regex_constants::nosubs);
+ return std::regex_search(str, re) ? 0 : -1;
+}
+
--- /dev/null
+#include <chrono>
+
+int main() {
+ typedef std::chrono::steady_clock Clock;
+ Clock::time_point tp = Clock::now();
+ ((void)tp);
+}
--- /dev/null
+#define HAVE_THREAD_SAFETY_ATTRIBUTES
+#include "../src/mutex.h"
+
+int main() {}
--- /dev/null
+# Build tool dependency policy
+
+To ensure the broadest compatibility when building the benchmark library, but
+still allow forward progress, we require any build tooling to be available for:
+
+* Debian stable AND
+* The last two Ubuntu LTS releases AND
+
+Currently, this means using build tool versions that are available for Ubuntu
+16.04 (Xenial), Ubuntu 18.04 (Bionic), and Debian stretch.
+
+_Note, [travis](.travis.yml) runs under Ubuntu 14.04 (Trusty) for linux builds._
+
+## cmake
+The current supported version is cmake 3.5.1 as of 2018-06-06.
+
+_Note, this version is also available for Ubuntu 14.04, the previous Ubuntu LTS
+release, as `cmake3`._
--- /dev/null
+# Assembly Tests
+
+The Benchmark library provides a number of functions whose primary
+purpose in to affect assembly generation, including `DoNotOptimize`
+and `ClobberMemory`. In addition there are other functions,
+such as `KeepRunning`, for which generating good assembly is paramount.
+
+For these functions it's important to have tests that verify the
+correctness and quality of the implementation. This requires testing
+the code generated by the compiler.
+
+This document describes how the Benchmark library tests compiler output,
+as well as how to properly write new tests.
+
+
+## Anatomy of a Test
+
+Writing a test has two steps:
+
+* Write the code you want to generate assembly for.
+* Add `// CHECK` lines to match against the verified assembly.
+
+Example:
+```c++
+
+// CHECK-LABEL: test_add:
+extern "C" int test_add() {
+ extern int ExternInt;
+ return ExternInt + 1;
+
+ // CHECK: movl ExternInt(%rip), %eax
+ // CHECK: addl %eax
+ // CHECK: ret
+}
+
+```
+
+#### LLVM Filecheck
+
+[LLVM's Filecheck](https://llvm.org/docs/CommandGuide/FileCheck.html)
+is used to test the generated assembly against the `// CHECK` lines
+specified in the tests source file. Please see the documentation
+linked above for information on how to write `CHECK` directives.
+
+#### Tips and Tricks:
+
+* Tests should match the minimal amount of output required to establish
+correctness. `CHECK` directives don't have to match on the exact next line
+after the previous match, so tests should omit checks for unimportant
+bits of assembly. ([`CHECK-NEXT`](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-next-directive)
+can be used to ensure a match occurs exactly after the previous match).
+
+* The tests are compiled with `-O3 -g0`. So we're only testing the
+optimized output.
+
+* The assembly output is further cleaned up using `tools/strip_asm.py`.
+This removes comments, assembler directives, and unused labels before
+the test is run.
+
+* The generated and stripped assembly file for a test is output under
+`<build-directory>/test/<test-name>.s`
+
+* Filecheck supports using [`CHECK` prefixes](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-check-prefixes)
+to specify lines that should only match in certain situations.
+The Benchmark tests use `CHECK-CLANG` and `CHECK-GNU` for lines that
+are only expected to match Clang or GCC's output respectively. Normal
+`CHECK` lines match against all compilers. (Note: `CHECK-NOT` and
+`CHECK-LABEL` are NOT prefixes. They are versions of non-prefixed
+`CHECK` lines)
+
+* Use `extern "C"` to disable name mangling for specific functions. This
+makes them easier to name in the `CHECK` lines.
+
+
+## Problems Writing Portable Tests
+
+Writing tests which check the code generated by a compiler are
+inherently non-portable. Different compilers and even different compiler
+versions may generate entirely different code. The Benchmark tests
+must tolerate this.
+
+LLVM Filecheck provides a number of mechanisms to help write
+"more portable" tests; including [matching using regular expressions](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-pattern-matching-syntax),
+allowing the creation of [named variables](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-variables)
+for later matching, and [checking non-sequential matches](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-dag-directive).
+
+#### Capturing Variables
+
+For example, say GCC stores a variable in a register but Clang stores
+it in memory. To write a test that tolerates both cases we "capture"
+the destination of the store, and then use the captured expression
+to write the remainder of the test.
+
+```c++
+// CHECK-LABEL: test_div_no_op_into_shr:
+extern "C" void test_div_no_op_into_shr(int value) {
+ int divisor = 2;
+ benchmark::DoNotOptimize(divisor); // hide the value from the optimizer
+ return value / divisor;
+
+ // CHECK: movl $2, [[DEST:.*]]
+ // CHECK: idivl [[DEST]]
+ // CHECK: ret
+}
+```
+
+#### Using Regular Expressions to Match Differing Output
+
+Often tests require testing assembly lines which may subtly differ
+between compilers or compiler versions. A common example of this
+is matching stack frame addresses. In this case regular expressions
+can be used to match the differing bits of output. For example:
+
+```c++
+int ExternInt;
+struct Point { int x, y, z; };
+
+// CHECK-LABEL: test_store_point:
+extern "C" void test_store_point() {
+ Point p{ExternInt, ExternInt, ExternInt};
+ benchmark::DoNotOptimize(p);
+
+ // CHECK: movl ExternInt(%rip), %eax
+ // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
+ // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
+ // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
+ // CHECK: ret
+}
+```
+
+## Current Requirements and Limitations
+
+The tests require Filecheck to be installed along the `PATH` of the
+build machine. Otherwise the tests will be disabled.
+
+Additionally, as mentioned in the previous section, codegen tests are
+inherently non-portable. Currently the tests are limited to:
+
+* x86_64 targets.
+* Compiled with GCC or Clang
+
+Further work could be done, at least on a limited basis, to extend the
+tests to other architectures and compilers (using `CHECK` prefixes).
+
+Furthermore, the tests fail for builds which specify additional flags
+that modify code generation, including `--coverage` or `-fsanitize=`.
+
--- /dev/null
+theme: jekyll-theme-hacker
\ No newline at end of file
--- /dev/null
+<a name="perf-counters" />
+
+# User-Requested Performance Counters
+
+When running benchmarks, the user may choose to request collection of
+performance counters. This may be useful in investigation scenarios - narrowing
+down the cause of a regression; or verifying that the underlying cause of a
+performance improvement matches expectations.
+
+This feature is available if:
+
+* The benchmark is run on an architecture featuring a Performance Monitoring
+ Unit (PMU),
+* The benchmark is compiled with support for collecting counters. Currently,
+ this requires [libpfm](http://perfmon2.sourceforge.net/) be available at build
+ time
+
+The feature does not require modifying benchmark code. Counter collection is
+handled at the boundaries where timer collection is also handled.
+
+To opt-in:
+
+* Install `libpfm4-dev`, e.g. `apt-get install libpfm4-dev`.
+* Enable the cmake flag BENCHMARK_ENABLE_LIBPFM.
+
+To use, pass a comma-separated list of counter names through the
+`--benchmark_perf_counters` flag. The names are decoded through libpfm - meaning,
+they are platform specific, but some (e.g. `CYCLES` or `INSTRUCTIONS`) are
+mapped by libpfm to platform-specifics - see libpfm
+[documentation](http://perfmon2.sourceforge.net/docs.html) for more details.
+
+The counter values are reported back through the [User Counters](../README.md#custom-counters)
+mechanism, meaning, they are available in all the formats (e.g. JSON) supported
+by User Counters.
\ No newline at end of file
--- /dev/null
+<a name="interleaving" />
+
+# Random Interleaving
+
+[Random Interleaving](https://github.com/google/benchmark/issues/1051) is a
+technique to lower run-to-run variance. It randomly interleaves repetitions of a
+microbenchmark with repetitions from other microbenchmarks in the same benchmark
+test. Data shows it is able to lower run-to-run variance by
+[40%](https://github.com/google/benchmark/issues/1051) on average.
+
+To use, you mainly need to set `--benchmark_enable_random_interleaving=true`,
+and optionally specify non-zero repetition count `--benchmark_repetitions=9`
+and optionally decrease the per-repetition time `--benchmark_min_time=0.1`.
--- /dev/null
+# How to release
+
+* Make sure you're on main and synced to HEAD
+* Ensure the project builds and tests run (sanity check only, obviously)
+ * `parallel -j0 exec ::: test/*_test` can help ensure everything at least
+ passes
+* Prepare release notes
+ * `git log $(git describe --abbrev=0 --tags)..HEAD` gives you the list of
+ commits between the last annotated tag and HEAD
+ * Pick the most interesting.
+* Create one last commit that updates the version saved in `CMakeLists.txt` to the release version you're creating. (This version will be used if benchmark is installed from the archive you'll be creating in the next step.)
+
+```
+project (benchmark VERSION 1.5.3 LANGUAGES CXX)
+```
+
+* Create a release through github's interface
+ * Note this will create a lightweight tag.
+ * Update this to an annotated tag:
+ * `git pull --tags`
+ * `git tag -a -f <tag> <tag>`
+ * `git push --force origin`
--- /dev/null
+# Benchmark Tools
+
+## compare.py
+
+The `compare.py` can be used to compare the result of benchmarks.
+
+### Dependencies
+The utility relies on the [scipy](https://www.scipy.org) package which can be installed using pip:
+```bash
+pip3 install -r requirements.txt
+```
+
+### Displaying aggregates only
+
+The switch `-a` / `--display_aggregates_only` can be used to control the
+displayment of the normal iterations vs the aggregates. When passed, it will
+be passthrough to the benchmark binaries to be run, and will be accounted for
+in the tool itself; only the aggregates will be displayed, but not normal runs.
+It only affects the display, the separate runs will still be used to calculate
+the U test.
+
+### Modes of operation
+
+There are three modes of operation:
+
+1. Just compare two benchmarks
+The program is invoked like:
+
+``` bash
+$ compare.py benchmarks <benchmark_baseline> <benchmark_contender> [benchmark options]...
+```
+Where `<benchmark_baseline>` and `<benchmark_contender>` either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.
+
+`[benchmark options]` will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal `--benchmark_*` parameters, or some custom parameters your binary takes.
+
+Example output:
+```
+$ ./compare.py benchmarks ./a.out ./a.out
+RUNNING: ./a.out --benchmark_out=/tmp/tmprBT5nW
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:16:44
+------------------------------------------------------
+Benchmark Time CPU Iterations
+------------------------------------------------------
+BM_memcpy/8 36 ns 36 ns 19101577 211.669MB/s
+BM_memcpy/64 76 ns 76 ns 9412571 800.199MB/s
+BM_memcpy/512 84 ns 84 ns 8249070 5.64771GB/s
+BM_memcpy/1024 116 ns 116 ns 6181763 8.19505GB/s
+BM_memcpy/8192 643 ns 643 ns 1062855 11.8636GB/s
+BM_copy/8 222 ns 222 ns 3137987 34.3772MB/s
+BM_copy/64 1608 ns 1608 ns 432758 37.9501MB/s
+BM_copy/512 12589 ns 12589 ns 54806 38.7867MB/s
+BM_copy/1024 25169 ns 25169 ns 27713 38.8003MB/s
+BM_copy/8192 201165 ns 201112 ns 3486 38.8466MB/s
+RUNNING: ./a.out --benchmark_out=/tmp/tmpt1wwG_
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:16:53
+------------------------------------------------------
+Benchmark Time CPU Iterations
+------------------------------------------------------
+BM_memcpy/8 36 ns 36 ns 19397903 211.255MB/s
+BM_memcpy/64 73 ns 73 ns 9691174 839.635MB/s
+BM_memcpy/512 85 ns 85 ns 8312329 5.60101GB/s
+BM_memcpy/1024 118 ns 118 ns 6438774 8.11608GB/s
+BM_memcpy/8192 656 ns 656 ns 1068644 11.6277GB/s
+BM_copy/8 223 ns 223 ns 3146977 34.2338MB/s
+BM_copy/64 1611 ns 1611 ns 435340 37.8751MB/s
+BM_copy/512 12622 ns 12622 ns 54818 38.6844MB/s
+BM_copy/1024 25257 ns 25239 ns 27779 38.6927MB/s
+BM_copy/8192 205013 ns 205010 ns 3479 38.108MB/s
+Comparing ./a.out to ./a.out
+Benchmark Time CPU Time Old Time New CPU Old CPU New
+------------------------------------------------------------------------------------------------------
+BM_memcpy/8 +0.0020 +0.0020 36 36 36 36
+BM_memcpy/64 -0.0468 -0.0470 76 73 76 73
+BM_memcpy/512 +0.0081 +0.0083 84 85 84 85
+BM_memcpy/1024 +0.0098 +0.0097 116 118 116 118
+BM_memcpy/8192 +0.0200 +0.0203 643 656 643 656
+BM_copy/8 +0.0046 +0.0042 222 223 222 223
+BM_copy/64 +0.0020 +0.0020 1608 1611 1608 1611
+BM_copy/512 +0.0027 +0.0026 12589 12622 12589 12622
+BM_copy/1024 +0.0035 +0.0028 25169 25257 25169 25239
+BM_copy/8192 +0.0191 +0.0194 201165 205013 201112 205010
+```
+
+What it does is for the every benchmark from the first run it looks for the benchmark with exactly the same name in the second run, and then compares the results. If the names differ, the benchmark is omitted from the diff.
+As you can note, the values in `Time` and `CPU` columns are calculated as `(new - old) / |old|`.
+
+2. Compare two different filters of one benchmark
+The program is invoked like:
+
+``` bash
+$ compare.py filters <benchmark> <filter_baseline> <filter_contender> [benchmark options]...
+```
+Where `<benchmark>` either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.
+
+Where `<filter_baseline>` and `<filter_contender>` are the same regex filters that you would pass to the `[--benchmark_filter=<regex>]` parameter of the benchmark binary.
+
+`[benchmark options]` will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal `--benchmark_*` parameters, or some custom parameters your binary takes.
+
+Example output:
+```
+$ ./compare.py filters ./a.out BM_memcpy BM_copy
+RUNNING: ./a.out --benchmark_filter=BM_memcpy --benchmark_out=/tmp/tmpBWKk0k
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:37:28
+------------------------------------------------------
+Benchmark Time CPU Iterations
+------------------------------------------------------
+BM_memcpy/8 36 ns 36 ns 17891491 211.215MB/s
+BM_memcpy/64 74 ns 74 ns 9400999 825.646MB/s
+BM_memcpy/512 87 ns 87 ns 8027453 5.46126GB/s
+BM_memcpy/1024 111 ns 111 ns 6116853 8.5648GB/s
+BM_memcpy/8192 657 ns 656 ns 1064679 11.6247GB/s
+RUNNING: ./a.out --benchmark_filter=BM_copy --benchmark_out=/tmp/tmpAvWcOM
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:37:33
+----------------------------------------------------
+Benchmark Time CPU Iterations
+----------------------------------------------------
+BM_copy/8 227 ns 227 ns 3038700 33.6264MB/s
+BM_copy/64 1640 ns 1640 ns 426893 37.2154MB/s
+BM_copy/512 12804 ns 12801 ns 55417 38.1444MB/s
+BM_copy/1024 25409 ns 25407 ns 27516 38.4365MB/s
+BM_copy/8192 202986 ns 202990 ns 3454 38.4871MB/s
+Comparing BM_memcpy to BM_copy (from ./a.out)
+Benchmark Time CPU Time Old Time New CPU Old CPU New
+--------------------------------------------------------------------------------------------------------------------
+[BM_memcpy vs. BM_copy]/8 +5.2829 +5.2812 36 227 36 227
+[BM_memcpy vs. BM_copy]/64 +21.1719 +21.1856 74 1640 74 1640
+[BM_memcpy vs. BM_copy]/512 +145.6487 +145.6097 87 12804 87 12801
+[BM_memcpy vs. BM_copy]/1024 +227.1860 +227.1776 111 25409 111 25407
+[BM_memcpy vs. BM_copy]/8192 +308.1664 +308.2898 657 202986 656 202990
+```
+
+As you can see, it applies filter to the benchmarks, both when running the benchmark, and before doing the diff. And to make the diff work, the matches are replaced with some common string. Thus, you can compare two different benchmark families within one benchmark binary.
+As you can note, the values in `Time` and `CPU` columns are calculated as `(new - old) / |old|`.
+
+3. Compare filter one from benchmark one to filter two from benchmark two:
+The program is invoked like:
+
+``` bash
+$ compare.py filters <benchmark_baseline> <filter_baseline> <benchmark_contender> <filter_contender> [benchmark options]...
+```
+
+Where `<benchmark_baseline>` and `<benchmark_contender>` either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.
+
+Where `<filter_baseline>` and `<filter_contender>` are the same regex filters that you would pass to the `[--benchmark_filter=<regex>]` parameter of the benchmark binary.
+
+`[benchmark options]` will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal `--benchmark_*` parameters, or some custom parameters your binary takes.
+
+Example output:
+```
+$ ./compare.py benchmarksfiltered ./a.out BM_memcpy ./a.out BM_copy
+RUNNING: ./a.out --benchmark_filter=BM_memcpy --benchmark_out=/tmp/tmp_FvbYg
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:38:27
+------------------------------------------------------
+Benchmark Time CPU Iterations
+------------------------------------------------------
+BM_memcpy/8 37 ns 37 ns 18953482 204.118MB/s
+BM_memcpy/64 74 ns 74 ns 9206578 828.245MB/s
+BM_memcpy/512 91 ns 91 ns 8086195 5.25476GB/s
+BM_memcpy/1024 120 ns 120 ns 5804513 7.95662GB/s
+BM_memcpy/8192 664 ns 664 ns 1028363 11.4948GB/s
+RUNNING: ./a.out --benchmark_filter=BM_copy --benchmark_out=/tmp/tmpDfL5iE
+Run on (8 X 4000 MHz CPU s)
+2017-11-07 21:38:32
+----------------------------------------------------
+Benchmark Time CPU Iterations
+----------------------------------------------------
+BM_copy/8 230 ns 230 ns 2985909 33.1161MB/s
+BM_copy/64 1654 ns 1653 ns 419408 36.9137MB/s
+BM_copy/512 13122 ns 13120 ns 53403 37.2156MB/s
+BM_copy/1024 26679 ns 26666 ns 26575 36.6218MB/s
+BM_copy/8192 215068 ns 215053 ns 3221 36.3283MB/s
+Comparing BM_memcpy (from ./a.out) to BM_copy (from ./a.out)
+Benchmark Time CPU Time Old Time New CPU Old CPU New
+--------------------------------------------------------------------------------------------------------------------
+[BM_memcpy vs. BM_copy]/8 +5.1649 +5.1637 37 230 37 230
+[BM_memcpy vs. BM_copy]/64 +21.4352 +21.4374 74 1654 74 1653
+[BM_memcpy vs. BM_copy]/512 +143.6022 +143.5865 91 13122 91 13120
+[BM_memcpy vs. BM_copy]/1024 +221.5903 +221.4790 120 26679 120 26666
+[BM_memcpy vs. BM_copy]/8192 +322.9059 +323.0096 664 215068 664 215053
+```
+This is a mix of the previous two modes, two (potentially different) benchmark binaries are run, and a different filter is applied to each one.
+As you can note, the values in `Time` and `CPU` columns are calculated as `(new - old) / |old|`.
+
+### U test
+
+If there is a sufficient repetition count of the benchmarks, the tool can do
+a [U Test](https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_test), of the
+null hypothesis that it is equally likely that a randomly selected value from
+one sample will be less than or greater than a randomly selected value from a
+second sample.
+
+If the calculated p-value is below this value is lower than the significance
+level alpha, then the result is said to be statistically significant and the
+null hypothesis is rejected. Which in other words means that the two benchmarks
+aren't identical.
+
+**WARNING**: requires **LARGE** (no less than 9) number of repetitions to be
+meaningful!
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Support for registering benchmarks for functions.
+
+/* Example usage:
+// Define a function that executes the code to be measured a
+// specified number of times:
+static void BM_StringCreation(benchmark::State& state) {
+ for (auto _ : state)
+ std::string empty_string;
+}
+
+// Register the function as a benchmark
+BENCHMARK(BM_StringCreation);
+
+// Define another benchmark
+static void BM_StringCopy(benchmark::State& state) {
+ std::string x = "hello";
+ for (auto _ : state)
+ std::string copy(x);
+}
+BENCHMARK(BM_StringCopy);
+
+// Augment the main() program to invoke benchmarks if specified
+// via the --benchmarks command line flag. E.g.,
+// my_unittest --benchmark_filter=all
+// my_unittest --benchmark_filter=BM_StringCreation
+// my_unittest --benchmark_filter=String
+// my_unittest --benchmark_filter='Copy|Creation'
+int main(int argc, char** argv) {
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
+
+// Sometimes a family of microbenchmarks can be implemented with
+// just one routine that takes an extra argument to specify which
+// one of the family of benchmarks to run. For example, the following
+// code defines a family of microbenchmarks for measuring the speed
+// of memcpy() calls of different lengths:
+
+static void BM_memcpy(benchmark::State& state) {
+ char* src = new char[state.range(0)]; char* dst = new char[state.range(0)];
+ memset(src, 'x', state.range(0));
+ for (auto _ : state)
+ memcpy(dst, src, state.range(0));
+ state.SetBytesProcessed(state.iterations() * state.range(0));
+ delete[] src; delete[] dst;
+}
+BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
+
+// The preceding code is quite repetitive, and can be replaced with the
+// following short-hand. The following invocation will pick a few
+// appropriate arguments in the specified range and will generate a
+// microbenchmark for each such argument.
+BENCHMARK(BM_memcpy)->Range(8, 8<<10);
+
+// You might have a microbenchmark that depends on two inputs. For
+// example, the following code defines a family of microbenchmarks for
+// measuring the speed of set insertion.
+static void BM_SetInsert(benchmark::State& state) {
+ set<int> data;
+ for (auto _ : state) {
+ state.PauseTiming();
+ data = ConstructRandomSet(state.range(0));
+ state.ResumeTiming();
+ for (int j = 0; j < state.range(1); ++j)
+ data.insert(RandomNumber());
+ }
+}
+BENCHMARK(BM_SetInsert)
+ ->Args({1<<10, 128})
+ ->Args({2<<10, 128})
+ ->Args({4<<10, 128})
+ ->Args({8<<10, 128})
+ ->Args({1<<10, 512})
+ ->Args({2<<10, 512})
+ ->Args({4<<10, 512})
+ ->Args({8<<10, 512});
+
+// The preceding code is quite repetitive, and can be replaced with
+// the following short-hand. The following macro will pick a few
+// appropriate arguments in the product of the two specified ranges
+// and will generate a microbenchmark for each such pair.
+BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {128, 512}});
+
+// For more complex patterns of inputs, passing a custom function
+// to Apply allows programmatic specification of an
+// arbitrary set of arguments to run the microbenchmark on.
+// The following example enumerates a dense range on
+// one parameter, and a sparse range on the second.
+static void CustomArguments(benchmark::internal::Benchmark* b) {
+ for (int i = 0; i <= 10; ++i)
+ for (int j = 32; j <= 1024*1024; j *= 8)
+ b->Args({i, j});
+}
+BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
+
+// Templated microbenchmarks work the same way:
+// Produce then consume 'size' messages 'iters' times
+// Measures throughput in the absence of multiprogramming.
+template <class Q> int BM_Sequential(benchmark::State& state) {
+ Q q;
+ typename Q::value_type v;
+ for (auto _ : state) {
+ for (int i = state.range(0); i--; )
+ q.push(v);
+ for (int e = state.range(0); e--; )
+ q.Wait(&v);
+ }
+ // actually messages, not bytes:
+ state.SetBytesProcessed(state.iterations() * state.range(0));
+}
+BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
+
+Use `Benchmark::MinTime(double t)` to set the minimum time used to run the
+benchmark. This option overrides the `benchmark_min_time` flag.
+
+void BM_test(benchmark::State& state) {
+ ... body ...
+}
+BENCHMARK(BM_test)->MinTime(2.0); // Run for at least 2 seconds.
+
+In a multithreaded test, it is guaranteed that none of the threads will start
+until all have reached the loop start, and all will have finished before any
+thread exits the loop body. As such, any global setup or teardown you want to
+do can be wrapped in a check against the thread index:
+
+static void BM_MultiThreaded(benchmark::State& state) {
+ if (state.thread_index == 0) {
+ // Setup code here.
+ }
+ for (auto _ : state) {
+ // Run the test as normal.
+ }
+ if (state.thread_index == 0) {
+ // Teardown code here.
+ }
+}
+BENCHMARK(BM_MultiThreaded)->Threads(4);
+
+
+If a benchmark runs a few milliseconds it may be hard to visually compare the
+measured times, since the output data is given in nanoseconds per default. In
+order to manually set the time unit, you can specify it manually:
+
+BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
+*/
+
+#ifndef BENCHMARK_BENCHMARK_H_
+#define BENCHMARK_BENCHMARK_H_
+
+// The _MSVC_LANG check should detect Visual Studio 2015 Update 3 and newer.
+#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+#define BENCHMARK_HAS_CXX11
+#endif
+
+// This _MSC_VER check should detect VS 2017 v15.3 and newer.
+#if __cplusplus >= 201703L || \
+ (defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L)
+#define BENCHMARK_HAS_CXX17
+#endif
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#if defined(BENCHMARK_HAS_CXX11)
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+#endif
+
+#if defined(_MSC_VER)
+#include <intrin.h> // for _ReadWriteBarrier
+#endif
+
+#ifndef BENCHMARK_HAS_CXX11
+#define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ TypeName& operator=(const TypeName&)
+#else
+#define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ TypeName& operator=(const TypeName&) = delete
+#endif
+
+#ifdef BENCHMARK_HAS_CXX17
+#define BENCHMARK_UNUSED [[maybe_unused]]
+#elif defined(__GNUC__) || defined(__clang__)
+#define BENCHMARK_UNUSED __attribute__((unused))
+#else
+#define BENCHMARK_UNUSED
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define BENCHMARK_ALWAYS_INLINE __attribute__((always_inline))
+#define BENCHMARK_NOEXCEPT noexcept
+#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
+#elif defined(_MSC_VER) && !defined(__clang__)
+#define BENCHMARK_ALWAYS_INLINE __forceinline
+#if _MSC_VER >= 1900
+#define BENCHMARK_NOEXCEPT noexcept
+#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
+#else
+#define BENCHMARK_NOEXCEPT
+#define BENCHMARK_NOEXCEPT_OP(x)
+#endif
+#define __func__ __FUNCTION__
+#else
+#define BENCHMARK_ALWAYS_INLINE
+#define BENCHMARK_NOEXCEPT
+#define BENCHMARK_NOEXCEPT_OP(x)
+#endif
+
+#define BENCHMARK_INTERNAL_TOSTRING2(x) #x
+#define BENCHMARK_INTERNAL_TOSTRING(x) BENCHMARK_INTERNAL_TOSTRING2(x)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define BENCHMARK_BUILTIN_EXPECT(x, y) __builtin_expect(x, y)
+#define BENCHMARK_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
+#else
+#define BENCHMARK_BUILTIN_EXPECT(x, y) x
+#define BENCHMARK_DEPRECATED_MSG(msg)
+#define BENCHMARK_WARNING_MSG(msg) \
+ __pragma(message(__FILE__ "(" BENCHMARK_INTERNAL_TOSTRING( \
+ __LINE__) ") : warning note: " msg))
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+#define BENCHMARK_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if defined(__GNUC__) || __has_builtin(__builtin_unreachable)
+#define BENCHMARK_UNREACHABLE() __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define BENCHMARK_UNREACHABLE() __assume(false)
+#else
+#define BENCHMARK_UNREACHABLE() ((void)0)
+#endif
+
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_OVERRIDE override
+#else
+#define BENCHMARK_OVERRIDE
+#endif
+
+namespace benchmark {
+class BenchmarkReporter;
+class MemoryManager;
+
+void Initialize(int* argc, char** argv);
+void Shutdown();
+
+// Report to stdout all arguments in 'argv' as unrecognized except the first.
+// Returns true there is at least on unrecognized argument (i.e. 'argc' > 1).
+bool ReportUnrecognizedArguments(int argc, char** argv);
+
+// Generate a list of benchmarks matching the specified --benchmark_filter flag
+// and if --benchmark_list_tests is specified return after printing the name
+// of each matching benchmark. Otherwise run each matching benchmark and
+// report the results.
+//
+// The second and third overload use the specified 'display_reporter' and
+// 'file_reporter' respectively. 'file_reporter' will write to the file
+// specified
+// by '--benchmark_output'. If '--benchmark_output' is not given the
+// 'file_reporter' is ignored.
+//
+// RETURNS: The number of matching benchmarks.
+size_t RunSpecifiedBenchmarks();
+size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter);
+size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
+ BenchmarkReporter* file_reporter);
+
+// Register a MemoryManager instance that will be used to collect and report
+// allocation measurements for benchmark runs.
+void RegisterMemoryManager(MemoryManager* memory_manager);
+
+// Add a key-value pair to output as part of the context stanza in the report.
+void AddCustomContext(const std::string& key, const std::string& value);
+
+namespace internal {
+class Benchmark;
+class BenchmarkImp;
+class BenchmarkFamilies;
+
+void UseCharPointer(char const volatile*);
+
+// Take ownership of the pointer and register the benchmark. Return the
+// registered benchmark.
+Benchmark* RegisterBenchmarkInternal(Benchmark*);
+
+// Ensure that the standard streams are properly initialized in every TU.
+int InitializeStreams();
+BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams();
+
+} // namespace internal
+
+#if (!defined(__GNUC__) && !defined(__clang__)) || defined(__pnacl__) || \
+ defined(__EMSCRIPTEN__)
+#define BENCHMARK_HAS_NO_INLINE_ASSEMBLY
+#endif
+
+// The DoNotOptimize(...) function can be used to prevent a value or
+// expression from being optimized away by the compiler. This function is
+// intended to add little to no overhead.
+// See: https://youtu.be/nXaxk27zwlk?t=2441
+#ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
+template <class Tp>
+inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
+ asm volatile("" : : "r,m"(value) : "memory");
+}
+
+template <class Tp>
+inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
+#if defined(__clang__)
+ asm volatile("" : "+r,m"(value) : : "memory");
+#else
+ asm volatile("" : "+m,r"(value) : : "memory");
+#endif
+}
+
+// Force the compiler to flush pending writes to global memory. Acts as an
+// effective read/write barrier
+inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() {
+ asm volatile("" : : : "memory");
+}
+#elif defined(_MSC_VER)
+template <class Tp>
+inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
+ internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
+ _ReadWriteBarrier();
+}
+
+inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() { _ReadWriteBarrier(); }
+#else
+template <class Tp>
+inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
+ internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
+}
+// FIXME Add ClobberMemory() for non-gnu and non-msvc compilers
+#endif
+
+// This class is used for user-defined counters.
+class Counter {
+ public:
+ enum Flags {
+ kDefaults = 0,
+ // Mark the counter as a rate. It will be presented divided
+ // by the duration of the benchmark.
+ kIsRate = 1U << 0U,
+ // Mark the counter as a thread-average quantity. It will be
+ // presented divided by the number of threads.
+ kAvgThreads = 1U << 1U,
+ // Mark the counter as a thread-average rate. See above.
+ kAvgThreadsRate = kIsRate | kAvgThreads,
+ // Mark the counter as a constant value, valid/same for *every* iteration.
+ // When reporting, it will be *multiplied* by the iteration count.
+ kIsIterationInvariant = 1U << 2U,
+ // Mark the counter as a constant rate.
+ // When reporting, it will be *multiplied* by the iteration count
+ // and then divided by the duration of the benchmark.
+ kIsIterationInvariantRate = kIsRate | kIsIterationInvariant,
+ // Mark the counter as a iteration-average quantity.
+ // It will be presented divided by the number of iterations.
+ kAvgIterations = 1U << 3U,
+ // Mark the counter as a iteration-average rate. See above.
+ kAvgIterationsRate = kIsRate | kAvgIterations,
+
+ // In the end, invert the result. This is always done last!
+ kInvert = 1U << 31U
+ };
+
+ enum OneK {
+ // 1'000 items per 1k
+ kIs1000 = 1000,
+ // 1'024 items per 1k
+ kIs1024 = 1024
+ };
+
+ double value;
+ Flags flags;
+ OneK oneK;
+
+ BENCHMARK_ALWAYS_INLINE
+ Counter(double v = 0., Flags f = kDefaults, OneK k = kIs1000)
+ : value(v), flags(f), oneK(k) {}
+
+ BENCHMARK_ALWAYS_INLINE operator double const&() const { return value; }
+ BENCHMARK_ALWAYS_INLINE operator double&() { return value; }
+};
+
+// A helper for user code to create unforeseen combinations of Flags, without
+// having to do this cast manually each time, or providing this operator.
+Counter::Flags inline operator|(const Counter::Flags& LHS,
+ const Counter::Flags& RHS) {
+ return static_cast<Counter::Flags>(static_cast<int>(LHS) |
+ static_cast<int>(RHS));
+}
+
+// This is the container for the user-defined counters.
+typedef std::map<std::string, Counter> UserCounters;
+
+// TimeUnit is passed to a benchmark in order to specify the order of magnitude
+// for the measured time.
+enum TimeUnit { kNanosecond, kMicrosecond, kMillisecond, kSecond };
+
+// BigO is passed to a benchmark in order to specify the asymptotic
+// computational
+// complexity for the benchmark. In case oAuto is selected, complexity will be
+// calculated automatically to the best fit.
+enum BigO { oNone, o1, oN, oNSquared, oNCubed, oLogN, oNLogN, oAuto, oLambda };
+
+typedef uint64_t IterationCount;
+
+// BigOFunc is passed to a benchmark in order to specify the asymptotic
+// computational complexity for the benchmark.
+typedef double(BigOFunc)(IterationCount);
+
+// StatisticsFunc is passed to a benchmark in order to compute some descriptive
+// statistics over all the measurements of some type
+typedef double(StatisticsFunc)(const std::vector<double>&);
+
+namespace internal {
+struct Statistics {
+ std::string name_;
+ StatisticsFunc* compute_;
+
+ Statistics(const std::string& name, StatisticsFunc* compute)
+ : name_(name), compute_(compute) {}
+};
+
+class BenchmarkInstance;
+class ThreadTimer;
+class ThreadManager;
+class PerfCountersMeasurement;
+
+enum AggregationReportMode
+#if defined(BENCHMARK_HAS_CXX11)
+ : unsigned
+#else
+#endif
+{
+ // The mode has not been manually specified
+ ARM_Unspecified = 0,
+ // The mode is user-specified.
+ // This may or may not be set when the following bit-flags are set.
+ ARM_Default = 1U << 0U,
+ // File reporter should only output aggregates.
+ ARM_FileReportAggregatesOnly = 1U << 1U,
+ // Display reporter should only output aggregates
+ ARM_DisplayReportAggregatesOnly = 1U << 2U,
+ // Both reporters should only display aggregates.
+ ARM_ReportAggregatesOnly =
+ ARM_FileReportAggregatesOnly | ARM_DisplayReportAggregatesOnly
+};
+
+} // namespace internal
+
+// State is passed to a running Benchmark and contains state for the
+// benchmark to use.
+class State {
+ public:
+ struct StateIterator;
+ friend struct StateIterator;
+
+ // Returns iterators used to run each iteration of a benchmark using a
+ // C++11 ranged-based for loop. These functions should not be called directly.
+ //
+ // REQUIRES: The benchmark has not started running yet. Neither begin nor end
+ // have been called previously.
+ //
+ // NOTE: KeepRunning may not be used after calling either of these functions.
+ BENCHMARK_ALWAYS_INLINE StateIterator begin();
+ BENCHMARK_ALWAYS_INLINE StateIterator end();
+
+ // Returns true if the benchmark should continue through another iteration.
+ // NOTE: A benchmark may not return from the test until KeepRunning() has
+ // returned false.
+ bool KeepRunning();
+
+ // Returns true iff the benchmark should run n more iterations.
+ // REQUIRES: 'n' > 0.
+ // NOTE: A benchmark must not return from the test until KeepRunningBatch()
+ // has returned false.
+ // NOTE: KeepRunningBatch() may overshoot by up to 'n' iterations.
+ //
+ // Intended usage:
+ // while (state.KeepRunningBatch(1000)) {
+ // // process 1000 elements
+ // }
+ bool KeepRunningBatch(IterationCount n);
+
+ // REQUIRES: timer is running and 'SkipWithError(...)' has not been called
+ // by the current thread.
+ // Stop the benchmark timer. If not called, the timer will be
+ // automatically stopped after the last iteration of the benchmark loop.
+ //
+ // For threaded benchmarks the PauseTiming() function only pauses the timing
+ // for the current thread.
+ //
+ // NOTE: The "real time" measurement is per-thread. If different threads
+ // report different measurements the largest one is reported.
+ //
+ // NOTE: PauseTiming()/ResumeTiming() are relatively
+ // heavyweight, and so their use should generally be avoided
+ // within each benchmark iteration, if possible.
+ void PauseTiming();
+
+ // REQUIRES: timer is not running and 'SkipWithError(...)' has not been called
+ // by the current thread.
+ // Start the benchmark timer. The timer is NOT running on entrance to the
+ // benchmark function. It begins running after control flow enters the
+ // benchmark loop.
+ //
+ // NOTE: PauseTiming()/ResumeTiming() are relatively
+ // heavyweight, and so their use should generally be avoided
+ // within each benchmark iteration, if possible.
+ void ResumeTiming();
+
+ // REQUIRES: 'SkipWithError(...)' has not been called previously by the
+ // current thread.
+ // Report the benchmark as resulting in an error with the specified 'msg'.
+ // After this call the user may explicitly 'return' from the benchmark.
+ //
+ // If the ranged-for style of benchmark loop is used, the user must explicitly
+ // break from the loop, otherwise all future iterations will be run.
+ // If the 'KeepRunning()' loop is used the current thread will automatically
+ // exit the loop at the end of the current iteration.
+ //
+ // For threaded benchmarks only the current thread stops executing and future
+ // calls to `KeepRunning()` will block until all threads have completed
+ // the `KeepRunning()` loop. If multiple threads report an error only the
+ // first error message is used.
+ //
+ // NOTE: Calling 'SkipWithError(...)' does not cause the benchmark to exit
+ // the current scope immediately. If the function is called from within
+ // the 'KeepRunning()' loop the current iteration will finish. It is the users
+ // responsibility to exit the scope as needed.
+ void SkipWithError(const char* msg);
+
+ // Returns true if an error has been reported with 'SkipWithError(...)'.
+ bool error_occurred() const { return error_occurred_; }
+
+ // REQUIRES: called exactly once per iteration of the benchmarking loop.
+ // Set the manually measured time for this benchmark iteration, which
+ // is used instead of automatically measured time if UseManualTime() was
+ // specified.
+ //
+ // For threaded benchmarks the final value will be set to the largest
+ // reported values.
+ void SetIterationTime(double seconds);
+
+ // Set the number of bytes processed by the current benchmark
+ // execution. This routine is typically called once at the end of a
+ // throughput oriented benchmark.
+ //
+ // REQUIRES: a benchmark has exited its benchmarking loop.
+ BENCHMARK_ALWAYS_INLINE
+ void SetBytesProcessed(int64_t bytes) {
+ counters["bytes_per_second"] =
+ Counter(static_cast<double>(bytes), Counter::kIsRate, Counter::kIs1024);
+ }
+
+ BENCHMARK_ALWAYS_INLINE
+ int64_t bytes_processed() const {
+ if (counters.find("bytes_per_second") != counters.end())
+ return static_cast<int64_t>(counters.at("bytes_per_second"));
+ return 0;
+ }
+
+ // If this routine is called with complexity_n > 0 and complexity report is
+ // requested for the
+ // family benchmark, then current benchmark will be part of the computation
+ // and complexity_n will
+ // represent the length of N.
+ BENCHMARK_ALWAYS_INLINE
+ void SetComplexityN(int64_t complexity_n) { complexity_n_ = complexity_n; }
+
+ BENCHMARK_ALWAYS_INLINE
+ int64_t complexity_length_n() const { return complexity_n_; }
+
+ // If this routine is called with items > 0, then an items/s
+ // label is printed on the benchmark report line for the currently
+ // executing benchmark. It is typically called at the end of a processing
+ // benchmark where a processing items/second output is desired.
+ //
+ // REQUIRES: a benchmark has exited its benchmarking loop.
+ BENCHMARK_ALWAYS_INLINE
+ void SetItemsProcessed(int64_t items) {
+ counters["items_per_second"] =
+ Counter(static_cast<double>(items), benchmark::Counter::kIsRate);
+ }
+
+ BENCHMARK_ALWAYS_INLINE
+ int64_t items_processed() const {
+ if (counters.find("items_per_second") != counters.end())
+ return static_cast<int64_t>(counters.at("items_per_second"));
+ return 0;
+ }
+
+ // If this routine is called, the specified label is printed at the
+ // end of the benchmark report line for the currently executing
+ // benchmark. Example:
+ // static void BM_Compress(benchmark::State& state) {
+ // ...
+ // double compress = input_size / output_size;
+ // state.SetLabel(StrFormat("compress:%.1f%%", 100.0*compression));
+ // }
+ // Produces output that looks like:
+ // BM_Compress 50 50 14115038 compress:27.3%
+ //
+ // REQUIRES: a benchmark has exited its benchmarking loop.
+ void SetLabel(const char* label);
+
+ void BENCHMARK_ALWAYS_INLINE SetLabel(const std::string& str) {
+ this->SetLabel(str.c_str());
+ }
+
+ // Range arguments for this run. CHECKs if the argument has been set.
+ BENCHMARK_ALWAYS_INLINE
+ int64_t range(std::size_t pos = 0) const {
+ assert(range_.size() > pos);
+ return range_[pos];
+ }
+
+ BENCHMARK_DEPRECATED_MSG("use 'range(0)' instead")
+ int64_t range_x() const { return range(0); }
+
+ BENCHMARK_DEPRECATED_MSG("use 'range(1)' instead")
+ int64_t range_y() const { return range(1); }
+
+ BENCHMARK_ALWAYS_INLINE
+ IterationCount iterations() const {
+ if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
+ return 0;
+ }
+ return max_iterations - total_iterations_ + batch_leftover_;
+ }
+
+ private
+ : // items we expect on the first cache line (ie 64 bytes of the struct)
+ // When total_iterations_ is 0, KeepRunning() and friends will return false.
+ // May be larger than max_iterations.
+ IterationCount total_iterations_;
+
+ // When using KeepRunningBatch(), batch_leftover_ holds the number of
+ // iterations beyond max_iters that were run. Used to track
+ // completed_iterations_ accurately.
+ IterationCount batch_leftover_;
+
+ public:
+ const IterationCount max_iterations;
+
+ private:
+ bool started_;
+ bool finished_;
+ bool error_occurred_;
+
+ private: // items we don't need on the first cache line
+ std::vector<int64_t> range_;
+
+ int64_t complexity_n_;
+
+ public:
+ // Container for user-defined counters.
+ UserCounters counters;
+ // Index of the executing thread. Values from [0, threads).
+ const int thread_index;
+ // Number of threads concurrently executing the benchmark.
+ const int threads;
+
+ private:
+ State(IterationCount max_iters, const std::vector<int64_t>& ranges,
+ int thread_i, int n_threads, internal::ThreadTimer* timer,
+ internal::ThreadManager* manager,
+ internal::PerfCountersMeasurement* perf_counters_measurement);
+
+ void StartKeepRunning();
+ // Implementation of KeepRunning() and KeepRunningBatch().
+ // is_batch must be true unless n is 1.
+ bool KeepRunningInternal(IterationCount n, bool is_batch);
+ void FinishKeepRunning();
+ internal::ThreadTimer* const timer_;
+ internal::ThreadManager* const manager_;
+ internal::PerfCountersMeasurement* const perf_counters_measurement_;
+
+ friend class internal::BenchmarkInstance;
+};
+
+inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunning() {
+ return KeepRunningInternal(1, /*is_batch=*/false);
+}
+
+inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunningBatch(IterationCount n) {
+ return KeepRunningInternal(n, /*is_batch=*/true);
+}
+
+inline BENCHMARK_ALWAYS_INLINE bool State::KeepRunningInternal(IterationCount n,
+ bool is_batch) {
+ // total_iterations_ is set to 0 by the constructor, and always set to a
+ // nonzero value by StartKepRunning().
+ assert(n > 0);
+ // n must be 1 unless is_batch is true.
+ assert(is_batch || n == 1);
+ if (BENCHMARK_BUILTIN_EXPECT(total_iterations_ >= n, true)) {
+ total_iterations_ -= n;
+ return true;
+ }
+ if (!started_) {
+ StartKeepRunning();
+ if (!error_occurred_ && total_iterations_ >= n) {
+ total_iterations_ -= n;
+ return true;
+ }
+ }
+ // For non-batch runs, total_iterations_ must be 0 by now.
+ if (is_batch && total_iterations_ != 0) {
+ batch_leftover_ = n - total_iterations_;
+ total_iterations_ = 0;
+ return true;
+ }
+ FinishKeepRunning();
+ return false;
+}
+
+struct State::StateIterator {
+ struct BENCHMARK_UNUSED Value {};
+ typedef std::forward_iterator_tag iterator_category;
+ typedef Value value_type;
+ typedef Value reference;
+ typedef Value pointer;
+ typedef std::ptrdiff_t difference_type;
+
+ private:
+ friend class State;
+ BENCHMARK_ALWAYS_INLINE
+ StateIterator() : cached_(0), parent_() {}
+
+ BENCHMARK_ALWAYS_INLINE
+ explicit StateIterator(State* st)
+ : cached_(st->error_occurred_ ? 0 : st->max_iterations), parent_(st) {}
+
+ public:
+ BENCHMARK_ALWAYS_INLINE
+ Value operator*() const { return Value(); }
+
+ BENCHMARK_ALWAYS_INLINE
+ StateIterator& operator++() {
+ assert(cached_ > 0);
+ --cached_;
+ return *this;
+ }
+
+ BENCHMARK_ALWAYS_INLINE
+ bool operator!=(StateIterator const&) const {
+ if (BENCHMARK_BUILTIN_EXPECT(cached_ != 0, true)) return true;
+ parent_->FinishKeepRunning();
+ return false;
+ }
+
+ private:
+ IterationCount cached_;
+ State* const parent_;
+};
+
+inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::begin() {
+ return StateIterator(this);
+}
+inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::end() {
+ StartKeepRunning();
+ return StateIterator();
+}
+
+namespace internal {
+
+typedef void(Function)(State&);
+
+// ------------------------------------------------------
+// Benchmark registration object. The BENCHMARK() macro expands
+// into an internal::Benchmark* object. Various methods can
+// be called on this object to change the properties of the benchmark.
+// Each method returns "this" so that multiple method calls can
+// chained into one expression.
+class Benchmark {
+ public:
+ virtual ~Benchmark();
+
+ // Note: the following methods all return "this" so that multiple
+ // method calls can be chained together in one expression.
+
+ // Specify the name of the benchmark
+ Benchmark* Name(const std::string& name);
+
+ // Run this benchmark once with "x" as the extra argument passed
+ // to the function.
+ // REQUIRES: The function passed to the constructor must accept an arg1.
+ Benchmark* Arg(int64_t x);
+
+ // Run this benchmark with the given time unit for the generated output report
+ Benchmark* Unit(TimeUnit unit);
+
+ // Run this benchmark once for a number of values picked from the
+ // range [start..limit]. (start and limit are always picked.)
+ // REQUIRES: The function passed to the constructor must accept an arg1.
+ Benchmark* Range(int64_t start, int64_t limit);
+
+ // Run this benchmark once for all values in the range [start..limit] with
+ // specific step
+ // REQUIRES: The function passed to the constructor must accept an arg1.
+ Benchmark* DenseRange(int64_t start, int64_t limit, int step = 1);
+
+ // Run this benchmark once with "args" as the extra arguments passed
+ // to the function.
+ // REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
+ Benchmark* Args(const std::vector<int64_t>& args);
+
+ // Equivalent to Args({x, y})
+ // NOTE: This is a legacy C++03 interface provided for compatibility only.
+ // New code should use 'Args'.
+ Benchmark* ArgPair(int64_t x, int64_t y) {
+ std::vector<int64_t> args;
+ args.push_back(x);
+ args.push_back(y);
+ return Args(args);
+ }
+
+ // Run this benchmark once for a number of values picked from the
+ // ranges [start..limit]. (starts and limits are always picked.)
+ // REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
+ Benchmark* Ranges(const std::vector<std::pair<int64_t, int64_t> >& ranges);
+
+ // Run this benchmark once for each combination of values in the (cartesian)
+ // product of the supplied argument lists.
+ // REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
+ Benchmark* ArgsProduct(const std::vector<std::vector<int64_t> >& arglists);
+
+ // Equivalent to ArgNames({name})
+ Benchmark* ArgName(const std::string& name);
+
+ // Set the argument names to display in the benchmark name. If not called,
+ // only argument values will be shown.
+ Benchmark* ArgNames(const std::vector<std::string>& names);
+
+ // Equivalent to Ranges({{lo1, hi1}, {lo2, hi2}}).
+ // NOTE: This is a legacy C++03 interface provided for compatibility only.
+ // New code should use 'Ranges'.
+ Benchmark* RangePair(int64_t lo1, int64_t hi1, int64_t lo2, int64_t hi2) {
+ std::vector<std::pair<int64_t, int64_t> > ranges;
+ ranges.push_back(std::make_pair(lo1, hi1));
+ ranges.push_back(std::make_pair(lo2, hi2));
+ return Ranges(ranges);
+ }
+
+ // Pass this benchmark object to *func, which can customize
+ // the benchmark by calling various methods like Arg, Args,
+ // Threads, etc.
+ Benchmark* Apply(void (*func)(Benchmark* benchmark));
+
+ // Set the range multiplier for non-dense range. If not called, the range
+ // multiplier kRangeMultiplier will be used.
+ Benchmark* RangeMultiplier(int multiplier);
+
+ // Set the minimum amount of time to use when running this benchmark. This
+ // option overrides the `benchmark_min_time` flag.
+ // REQUIRES: `t > 0` and `Iterations` has not been called on this benchmark.
+ Benchmark* MinTime(double t);
+
+ // Specify the amount of iterations that should be run by this benchmark.
+ // REQUIRES: 'n > 0' and `MinTime` has not been called on this benchmark.
+ //
+ // NOTE: This function should only be used when *exact* iteration control is
+ // needed and never to control or limit how long a benchmark runs, where
+ // `--benchmark_min_time=N` or `MinTime(...)` should be used instead.
+ Benchmark* Iterations(IterationCount n);
+
+ // Specify the amount of times to repeat this benchmark. This option overrides
+ // the `benchmark_repetitions` flag.
+ // REQUIRES: `n > 0`
+ Benchmark* Repetitions(int n);
+
+ // Specify if each repetition of the benchmark should be reported separately
+ // or if only the final statistics should be reported. If the benchmark
+ // is not repeated then the single result is always reported.
+ // Applies to *ALL* reporters (display and file).
+ Benchmark* ReportAggregatesOnly(bool value = true);
+
+ // Same as ReportAggregatesOnly(), but applies to display reporter only.
+ Benchmark* DisplayAggregatesOnly(bool value = true);
+
+ // By default, the CPU time is measured only for the main thread, which may
+ // be unrepresentative if the benchmark uses threads internally. If called,
+ // the total CPU time spent by all the threads will be measured instead.
+ // By default, the only the main thread CPU time will be measured.
+ Benchmark* MeasureProcessCPUTime();
+
+ // If a particular benchmark should use the Wall clock instead of the CPU time
+ // (be it either the CPU time of the main thread only (default), or the
+ // total CPU usage of the benchmark), call this method. If called, the elapsed
+ // (wall) time will be used to control how many iterations are run, and in the
+ // printing of items/second or MB/seconds values.
+ // If not called, the CPU time used by the benchmark will be used.
+ Benchmark* UseRealTime();
+
+ // If a benchmark must measure time manually (e.g. if GPU execution time is
+ // being
+ // measured), call this method. If called, each benchmark iteration should
+ // call
+ // SetIterationTime(seconds) to report the measured time, which will be used
+ // to control how many iterations are run, and in the printing of items/second
+ // or MB/second values.
+ Benchmark* UseManualTime();
+
+ // Set the asymptotic computational complexity for the benchmark. If called
+ // the asymptotic computational complexity will be shown on the output.
+ Benchmark* Complexity(BigO complexity = benchmark::oAuto);
+
+ // Set the asymptotic computational complexity for the benchmark. If called
+ // the asymptotic computational complexity will be shown on the output.
+ Benchmark* Complexity(BigOFunc* complexity);
+
+ // Add this statistics to be computed over all the values of benchmark run
+ Benchmark* ComputeStatistics(std::string name, StatisticsFunc* statistics);
+
+ // Support for running multiple copies of the same benchmark concurrently
+ // in multiple threads. This may be useful when measuring the scaling
+ // of some piece of code.
+
+ // Run one instance of this benchmark concurrently in t threads.
+ Benchmark* Threads(int t);
+
+ // Pick a set of values T from [min_threads,max_threads].
+ // min_threads and max_threads are always included in T. Run this
+ // benchmark once for each value in T. The benchmark run for a
+ // particular value t consists of t threads running the benchmark
+ // function concurrently. For example, consider:
+ // BENCHMARK(Foo)->ThreadRange(1,16);
+ // This will run the following benchmarks:
+ // Foo in 1 thread
+ // Foo in 2 threads
+ // Foo in 4 threads
+ // Foo in 8 threads
+ // Foo in 16 threads
+ Benchmark* ThreadRange(int min_threads, int max_threads);
+
+ // For each value n in the range, run this benchmark once using n threads.
+ // min_threads and max_threads are always included in the range.
+ // stride specifies the increment. E.g. DenseThreadRange(1, 8, 3) starts
+ // a benchmark with 1, 4, 7 and 8 threads.
+ Benchmark* DenseThreadRange(int min_threads, int max_threads, int stride = 1);
+
+ // Equivalent to ThreadRange(NumCPUs(), NumCPUs())
+ Benchmark* ThreadPerCpu();
+
+ virtual void Run(State& state) = 0;
+
+ protected:
+ explicit Benchmark(const char* name);
+ Benchmark(Benchmark const&);
+ void SetName(const char* name);
+
+ int ArgsCnt() const;
+
+ private:
+ friend class BenchmarkFamilies;
+ friend class BenchmarkInstance;
+
+ std::string name_;
+ AggregationReportMode aggregation_report_mode_;
+ std::vector<std::string> arg_names_; // Args for all benchmark runs
+ std::vector<std::vector<int64_t> > args_; // Args for all benchmark runs
+ TimeUnit time_unit_;
+ int range_multiplier_;
+ double min_time_;
+ IterationCount iterations_;
+ int repetitions_;
+ bool measure_process_cpu_time_;
+ bool use_real_time_;
+ bool use_manual_time_;
+ BigO complexity_;
+ BigOFunc* complexity_lambda_;
+ std::vector<Statistics> statistics_;
+ std::vector<int> thread_counts_;
+
+ Benchmark& operator=(Benchmark const&);
+};
+
+} // namespace internal
+
+// Create and register a benchmark with the specified 'name' that invokes
+// the specified functor 'fn'.
+//
+// RETURNS: A pointer to the registered benchmark.
+internal::Benchmark* RegisterBenchmark(const char* name,
+ internal::Function* fn);
+
+#if defined(BENCHMARK_HAS_CXX11)
+template <class Lambda>
+internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn);
+#endif
+
+// Remove all registered benchmarks. All pointers to previously registered
+// benchmarks are invalidated.
+void ClearRegisteredBenchmarks();
+
+namespace internal {
+// The class used to hold all Benchmarks created from static function.
+// (ie those created using the BENCHMARK(...) macros.
+class FunctionBenchmark : public Benchmark {
+ public:
+ FunctionBenchmark(const char* name, Function* func)
+ : Benchmark(name), func_(func) {}
+
+ virtual void Run(State& st) BENCHMARK_OVERRIDE;
+
+ private:
+ Function* func_;
+};
+
+#ifdef BENCHMARK_HAS_CXX11
+template <class Lambda>
+class LambdaBenchmark : public Benchmark {
+ public:
+ virtual void Run(State& st) BENCHMARK_OVERRIDE { lambda_(st); }
+
+ private:
+ template <class OLambda>
+ LambdaBenchmark(const char* name, OLambda&& lam)
+ : Benchmark(name), lambda_(std::forward<OLambda>(lam)) {}
+
+ LambdaBenchmark(LambdaBenchmark const&) = delete;
+
+ private:
+ template <class Lam>
+ friend Benchmark* ::benchmark::RegisterBenchmark(const char*, Lam&&);
+
+ Lambda lambda_;
+};
+#endif
+
+} // namespace internal
+
+inline internal::Benchmark* RegisterBenchmark(const char* name,
+ internal::Function* fn) {
+ return internal::RegisterBenchmarkInternal(
+ ::new internal::FunctionBenchmark(name, fn));
+}
+
+#ifdef BENCHMARK_HAS_CXX11
+template <class Lambda>
+internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn) {
+ using BenchType =
+ internal::LambdaBenchmark<typename std::decay<Lambda>::type>;
+ return internal::RegisterBenchmarkInternal(
+ ::new BenchType(name, std::forward<Lambda>(fn)));
+}
+#endif
+
+#if defined(BENCHMARK_HAS_CXX11) && \
+ (!defined(BENCHMARK_GCC_VERSION) || BENCHMARK_GCC_VERSION >= 409)
+template <class Lambda, class... Args>
+internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn,
+ Args&&... args) {
+ return benchmark::RegisterBenchmark(
+ name, [=](benchmark::State& st) { fn(st, args...); });
+}
+#else
+#define BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
+#endif
+
+// The base class for all fixture tests.
+class Fixture : public internal::Benchmark {
+ public:
+ Fixture() : internal::Benchmark("") {}
+
+ virtual void Run(State& st) BENCHMARK_OVERRIDE {
+ this->SetUp(st);
+ this->BenchmarkCase(st);
+ this->TearDown(st);
+ }
+
+ // These will be deprecated ...
+ virtual void SetUp(const State&) {}
+ virtual void TearDown(const State&) {}
+ // ... In favor of these.
+ virtual void SetUp(State& st) { SetUp(const_cast<const State&>(st)); }
+ virtual void TearDown(State& st) { TearDown(const_cast<const State&>(st)); }
+
+ protected:
+ virtual void BenchmarkCase(State&) = 0;
+};
+
+} // namespace benchmark
+
+// ------------------------------------------------------
+// Macro to register benchmarks
+
+// Check that __COUNTER__ is defined and that __COUNTER__ increases by 1
+// every time it is expanded. X + 1 == X + 0 is used in case X is defined to be
+// empty. If X is empty the expression becomes (+1 == +0).
+#if defined(__COUNTER__) && (__COUNTER__ + 1 == __COUNTER__ + 0)
+#define BENCHMARK_PRIVATE_UNIQUE_ID __COUNTER__
+#else
+#define BENCHMARK_PRIVATE_UNIQUE_ID __LINE__
+#endif
+
+// Helpers for generating unique variable names
+#define BENCHMARK_PRIVATE_NAME(n) \
+ BENCHMARK_PRIVATE_CONCAT(benchmark_uniq_, BENCHMARK_PRIVATE_UNIQUE_ID, n)
+#define BENCHMARK_PRIVATE_CONCAT(a, b, c) BENCHMARK_PRIVATE_CONCAT2(a, b, c)
+#define BENCHMARK_PRIVATE_CONCAT2(a, b, c) a##b##c
+// Helper for concatenation with macro name expansion
+#define BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method) \
+ BaseClass##_##Method##_Benchmark
+
+#define BENCHMARK_PRIVATE_DECLARE(n) \
+ static ::benchmark::internal::Benchmark* BENCHMARK_PRIVATE_NAME(n) \
+ BENCHMARK_UNUSED
+
+#define BENCHMARK(n) \
+ BENCHMARK_PRIVATE_DECLARE(n) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ new ::benchmark::internal::FunctionBenchmark(#n, n)))
+
+// Old-style macros
+#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
+#define BENCHMARK_WITH_ARG2(n, a1, a2) BENCHMARK(n)->Args({(a1), (a2)})
+#define BENCHMARK_WITH_UNIT(n, t) BENCHMARK(n)->Unit((t))
+#define BENCHMARK_RANGE(n, lo, hi) BENCHMARK(n)->Range((lo), (hi))
+#define BENCHMARK_RANGE2(n, l1, h1, l2, h2) \
+ BENCHMARK(n)->RangePair({{(l1), (h1)}, {(l2), (h2)}})
+
+#ifdef BENCHMARK_HAS_CXX11
+
+// Register a benchmark which invokes the function specified by `func`
+// with the additional arguments specified by `...`.
+//
+// For example:
+//
+// template <class ...ExtraArgs>`
+// void BM_takes_args(benchmark::State& state, ExtraArgs&&... extra_args) {
+// [...]
+//}
+// /* Registers a benchmark named "BM_takes_args/int_string_test` */
+// BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));
+#define BENCHMARK_CAPTURE(func, test_case_name, ...) \
+ BENCHMARK_PRIVATE_DECLARE(func) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ new ::benchmark::internal::FunctionBenchmark( \
+ #func "/" #test_case_name, \
+ [](::benchmark::State& st) { func(st, __VA_ARGS__); })))
+
+#endif // BENCHMARK_HAS_CXX11
+
+// This will register a benchmark for a templatized function. For example:
+//
+// template<int arg>
+// void BM_Foo(int iters);
+//
+// BENCHMARK_TEMPLATE(BM_Foo, 1);
+//
+// will register BM_Foo<1> as a benchmark.
+#define BENCHMARK_TEMPLATE1(n, a) \
+ BENCHMARK_PRIVATE_DECLARE(n) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ new ::benchmark::internal::FunctionBenchmark(#n "<" #a ">", n<a>)))
+
+#define BENCHMARK_TEMPLATE2(n, a, b) \
+ BENCHMARK_PRIVATE_DECLARE(n) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ new ::benchmark::internal::FunctionBenchmark(#n "<" #a "," #b ">", \
+ n<a, b>)))
+
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_TEMPLATE(n, ...) \
+ BENCHMARK_PRIVATE_DECLARE(n) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ new ::benchmark::internal::FunctionBenchmark( \
+ #n "<" #__VA_ARGS__ ">", n<__VA_ARGS__>)))
+#else
+#define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a)
+#endif
+
+#define BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
+ class BaseClass##_##Method##_Benchmark : public BaseClass { \
+ public: \
+ BaseClass##_##Method##_Benchmark() : BaseClass() { \
+ this->SetName(#BaseClass "/" #Method); \
+ } \
+ \
+ protected: \
+ virtual void BenchmarkCase(::benchmark::State&) BENCHMARK_OVERRIDE; \
+ };
+
+#define BENCHMARK_TEMPLATE1_PRIVATE_DECLARE_F(BaseClass, Method, a) \
+ class BaseClass##_##Method##_Benchmark : public BaseClass<a> { \
+ public: \
+ BaseClass##_##Method##_Benchmark() : BaseClass<a>() { \
+ this->SetName(#BaseClass "<" #a ">/" #Method); \
+ } \
+ \
+ protected: \
+ virtual void BenchmarkCase(::benchmark::State&) BENCHMARK_OVERRIDE; \
+ };
+
+#define BENCHMARK_TEMPLATE2_PRIVATE_DECLARE_F(BaseClass, Method, a, b) \
+ class BaseClass##_##Method##_Benchmark : public BaseClass<a, b> { \
+ public: \
+ BaseClass##_##Method##_Benchmark() : BaseClass<a, b>() { \
+ this->SetName(#BaseClass "<" #a "," #b ">/" #Method); \
+ } \
+ \
+ protected: \
+ virtual void BenchmarkCase(::benchmark::State&) BENCHMARK_OVERRIDE; \
+ };
+
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_TEMPLATE_PRIVATE_DECLARE_F(BaseClass, Method, ...) \
+ class BaseClass##_##Method##_Benchmark : public BaseClass<__VA_ARGS__> { \
+ public: \
+ BaseClass##_##Method##_Benchmark() : BaseClass<__VA_ARGS__>() { \
+ this->SetName(#BaseClass "<" #__VA_ARGS__ ">/" #Method); \
+ } \
+ \
+ protected: \
+ virtual void BenchmarkCase(::benchmark::State&) BENCHMARK_OVERRIDE; \
+ };
+#else
+#define BENCHMARK_TEMPLATE_PRIVATE_DECLARE_F(n, a) \
+ BENCHMARK_TEMPLATE1_PRIVATE_DECLARE_F(n, a)
+#endif
+
+#define BENCHMARK_DEFINE_F(BaseClass, Method) \
+ BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#define BENCHMARK_TEMPLATE1_DEFINE_F(BaseClass, Method, a) \
+ BENCHMARK_TEMPLATE1_PRIVATE_DECLARE_F(BaseClass, Method, a) \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#define BENCHMARK_TEMPLATE2_DEFINE_F(BaseClass, Method, a, b) \
+ BENCHMARK_TEMPLATE2_PRIVATE_DECLARE_F(BaseClass, Method, a, b) \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_TEMPLATE_DEFINE_F(BaseClass, Method, ...) \
+ BENCHMARK_TEMPLATE_PRIVATE_DECLARE_F(BaseClass, Method, __VA_ARGS__) \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+#else
+#define BENCHMARK_TEMPLATE_DEFINE_F(BaseClass, Method, a) \
+ BENCHMARK_TEMPLATE1_DEFINE_F(BaseClass, Method, a)
+#endif
+
+#define BENCHMARK_REGISTER_F(BaseClass, Method) \
+ BENCHMARK_PRIVATE_REGISTER_F(BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method))
+
+#define BENCHMARK_PRIVATE_REGISTER_F(TestName) \
+ BENCHMARK_PRIVATE_DECLARE(TestName) = \
+ (::benchmark::internal::RegisterBenchmarkInternal(new TestName()))
+
+// This macro will define and register a benchmark within a fixture class.
+#define BENCHMARK_F(BaseClass, Method) \
+ BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
+ BENCHMARK_REGISTER_F(BaseClass, Method); \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#define BENCHMARK_TEMPLATE1_F(BaseClass, Method, a) \
+ BENCHMARK_TEMPLATE1_PRIVATE_DECLARE_F(BaseClass, Method, a) \
+ BENCHMARK_REGISTER_F(BaseClass, Method); \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#define BENCHMARK_TEMPLATE2_F(BaseClass, Method, a, b) \
+ BENCHMARK_TEMPLATE2_PRIVATE_DECLARE_F(BaseClass, Method, a, b) \
+ BENCHMARK_REGISTER_F(BaseClass, Method); \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+
+#ifdef BENCHMARK_HAS_CXX11
+#define BENCHMARK_TEMPLATE_F(BaseClass, Method, ...) \
+ BENCHMARK_TEMPLATE_PRIVATE_DECLARE_F(BaseClass, Method, __VA_ARGS__) \
+ BENCHMARK_REGISTER_F(BaseClass, Method); \
+ void BENCHMARK_PRIVATE_CONCAT_NAME(BaseClass, Method)::BenchmarkCase
+#else
+#define BENCHMARK_TEMPLATE_F(BaseClass, Method, a) \
+ BENCHMARK_TEMPLATE1_F(BaseClass, Method, a)
+#endif
+
+// Helper macro to create a main routine in a test that runs the benchmarks
+#define BENCHMARK_MAIN() \
+ int main(int argc, char** argv) { \
+ ::benchmark::Initialize(&argc, argv); \
+ if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; \
+ ::benchmark::RunSpecifiedBenchmarks(); \
+ ::benchmark::Shutdown(); \
+ return 0; \
+ } \
+ int main(int, char**)
+
+// ------------------------------------------------------
+// Benchmark Reporters
+
+namespace benchmark {
+
+struct CPUInfo {
+ struct CacheInfo {
+ std::string type;
+ int level;
+ int size;
+ int num_sharing;
+ };
+
+ enum Scaling {
+ UNKNOWN,
+ ENABLED,
+ DISABLED
+ };
+
+ int num_cpus;
+ Scaling scaling;
+ double cycles_per_second;
+ std::vector<CacheInfo> caches;
+ std::vector<double> load_avg;
+
+ static const CPUInfo& Get();
+
+ private:
+ CPUInfo();
+ BENCHMARK_DISALLOW_COPY_AND_ASSIGN(CPUInfo);
+};
+
+// Adding Struct for System Information
+struct SystemInfo {
+ std::string name;
+ static const SystemInfo& Get();
+
+ private:
+ SystemInfo();
+ BENCHMARK_DISALLOW_COPY_AND_ASSIGN(SystemInfo);
+};
+
+// BenchmarkName contains the components of the Benchmark's name
+// which allows individual fields to be modified or cleared before
+// building the final name using 'str()'.
+struct BenchmarkName {
+ std::string function_name;
+ std::string args;
+ std::string min_time;
+ std::string iterations;
+ std::string repetitions;
+ std::string time_type;
+ std::string threads;
+
+ // Return the full name of the benchmark with each non-empty
+ // field separated by a '/'
+ std::string str() const;
+};
+
+// Interface for custom benchmark result printers.
+// By default, benchmark reports are printed to stdout. However an application
+// can control the destination of the reports by calling
+// RunSpecifiedBenchmarks and passing it a custom reporter object.
+// The reporter object must implement the following interface.
+class BenchmarkReporter {
+ public:
+ struct Context {
+ CPUInfo const& cpu_info;
+ SystemInfo const& sys_info;
+ // The number of chars in the longest benchmark name.
+ size_t name_field_width;
+ static const char* executable_name;
+ Context();
+ };
+
+ struct Run {
+ static const int64_t no_repetition_index = -1;
+ enum RunType { RT_Iteration, RT_Aggregate };
+
+ Run()
+ : run_type(RT_Iteration),
+ error_occurred(false),
+ iterations(1),
+ threads(1),
+ time_unit(kNanosecond),
+ real_accumulated_time(0),
+ cpu_accumulated_time(0),
+ max_heapbytes_used(0),
+ complexity(oNone),
+ complexity_lambda(),
+ complexity_n(0),
+ report_big_o(false),
+ report_rms(false),
+ counters(),
+ has_memory_result(false),
+ allocs_per_iter(0.0),
+ max_bytes_used(0) {}
+
+ std::string benchmark_name() const;
+ BenchmarkName run_name;
+ int64_t family_index;
+ int64_t per_family_instance_index;
+ RunType run_type;
+ std::string aggregate_name;
+ std::string report_label; // Empty if not set by benchmark.
+ bool error_occurred;
+ std::string error_message;
+
+ IterationCount iterations;
+ int64_t threads;
+ int64_t repetition_index;
+ int64_t repetitions;
+ TimeUnit time_unit;
+ double real_accumulated_time;
+ double cpu_accumulated_time;
+
+ // Return a value representing the real time per iteration in the unit
+ // specified by 'time_unit'.
+ // NOTE: If 'iterations' is zero the returned value represents the
+ // accumulated time.
+ double GetAdjustedRealTime() const;
+
+ // Return a value representing the cpu time per iteration in the unit
+ // specified by 'time_unit'.
+ // NOTE: If 'iterations' is zero the returned value represents the
+ // accumulated time.
+ double GetAdjustedCPUTime() const;
+
+ // This is set to 0.0 if memory tracing is not enabled.
+ double max_heapbytes_used;
+
+ // Keep track of arguments to compute asymptotic complexity
+ BigO complexity;
+ BigOFunc* complexity_lambda;
+ int64_t complexity_n;
+
+ // what statistics to compute from the measurements
+ const std::vector<internal::Statistics>* statistics;
+
+ // Inform print function whether the current run is a complexity report
+ bool report_big_o;
+ bool report_rms;
+
+ UserCounters counters;
+
+ // Memory metrics.
+ bool has_memory_result;
+ double allocs_per_iter;
+ int64_t max_bytes_used;
+ };
+
+ struct PerFamilyRunReports {
+ PerFamilyRunReports() : num_runs_total(0), num_runs_done(0) {}
+
+ // How many runs will all instances of this benchmark perform?
+ int num_runs_total;
+
+ // How many runs have happened already?
+ int num_runs_done;
+
+ // The reports about (non-errneous!) runs of this family.
+ std::vector<BenchmarkReporter::Run> Runs;
+ };
+
+ // Construct a BenchmarkReporter with the output stream set to 'std::cout'
+ // and the error stream set to 'std::cerr'
+ BenchmarkReporter();
+
+ // Called once for every suite of benchmarks run.
+ // The parameter "context" contains information that the
+ // reporter may wish to use when generating its report, for example the
+ // platform under which the benchmarks are running. The benchmark run is
+ // never started if this function returns false, allowing the reporter
+ // to skip runs based on the context information.
+ virtual bool ReportContext(const Context& context) = 0;
+
+ // Called once for each group of benchmark runs, gives information about
+ // cpu-time and heap memory usage during the benchmark run. If the group
+ // of runs contained more than two entries then 'report' contains additional
+ // elements representing the mean and standard deviation of those runs.
+ // Additionally if this group of runs was the last in a family of benchmarks
+ // 'reports' contains additional entries representing the asymptotic
+ // complexity and RMS of that benchmark family.
+ virtual void ReportRuns(const std::vector<Run>& report) = 0;
+
+ // Called once and only once after ever group of benchmarks is run and
+ // reported.
+ virtual void Finalize() {}
+
+ // REQUIRES: The object referenced by 'out' is valid for the lifetime
+ // of the reporter.
+ void SetOutputStream(std::ostream* out) {
+ assert(out);
+ output_stream_ = out;
+ }
+
+ // REQUIRES: The object referenced by 'err' is valid for the lifetime
+ // of the reporter.
+ void SetErrorStream(std::ostream* err) {
+ assert(err);
+ error_stream_ = err;
+ }
+
+ std::ostream& GetOutputStream() const { return *output_stream_; }
+
+ std::ostream& GetErrorStream() const { return *error_stream_; }
+
+ virtual ~BenchmarkReporter();
+
+ // Write a human readable string to 'out' representing the specified
+ // 'context'.
+ // REQUIRES: 'out' is non-null.
+ static void PrintBasicContext(std::ostream* out, Context const& context);
+
+ private:
+ std::ostream* output_stream_;
+ std::ostream* error_stream_;
+};
+
+// Simple reporter that outputs benchmark data to the console. This is the
+// default reporter used by RunSpecifiedBenchmarks().
+class ConsoleReporter : public BenchmarkReporter {
+ public:
+ enum OutputOptions {
+ OO_None = 0,
+ OO_Color = 1,
+ OO_Tabular = 2,
+ OO_ColorTabular = OO_Color | OO_Tabular,
+ OO_Defaults = OO_ColorTabular
+ };
+ explicit ConsoleReporter(OutputOptions opts_ = OO_Defaults)
+ : output_options_(opts_),
+ name_field_width_(0),
+ prev_counters_(),
+ printed_header_(false) {}
+
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE;
+ virtual void ReportRuns(const std::vector<Run>& reports) BENCHMARK_OVERRIDE;
+
+ protected:
+ virtual void PrintRunData(const Run& report);
+ virtual void PrintHeader(const Run& report);
+
+ OutputOptions output_options_;
+ size_t name_field_width_;
+ UserCounters prev_counters_;
+ bool printed_header_;
+};
+
+class JSONReporter : public BenchmarkReporter {
+ public:
+ JSONReporter() : first_report_(true) {}
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE;
+ virtual void ReportRuns(const std::vector<Run>& reports) BENCHMARK_OVERRIDE;
+ virtual void Finalize() BENCHMARK_OVERRIDE;
+
+ private:
+ void PrintRunData(const Run& report);
+
+ bool first_report_;
+};
+
+class BENCHMARK_DEPRECATED_MSG(
+ "The CSV Reporter will be removed in a future release") CSVReporter
+ : public BenchmarkReporter {
+ public:
+ CSVReporter() : printed_header_(false) {}
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE;
+ virtual void ReportRuns(const std::vector<Run>& reports) BENCHMARK_OVERRIDE;
+
+ private:
+ void PrintRunData(const Run& report);
+
+ bool printed_header_;
+ std::set<std::string> user_counter_names_;
+};
+
+// If a MemoryManager is registered, it can be used to collect and report
+// allocation metrics for a run of the benchmark.
+class MemoryManager {
+ public:
+ struct Result {
+ Result() : num_allocs(0), max_bytes_used(0) {}
+
+ // The number of allocations made in total between Start and Stop.
+ int64_t num_allocs;
+
+ // The peak memory use between Start and Stop.
+ int64_t max_bytes_used;
+ };
+
+ virtual ~MemoryManager() {}
+
+ // Implement this to start recording allocation information.
+ virtual void Start() = 0;
+
+ // Implement this to stop recording and fill out the given Result structure.
+ virtual void Stop(Result* result) = 0;
+};
+
+inline const char* GetTimeUnitString(TimeUnit unit) {
+ switch (unit) {
+ case kSecond:
+ return "s";
+ case kMillisecond:
+ return "ms";
+ case kMicrosecond:
+ return "us";
+ case kNanosecond:
+ return "ns";
+ }
+ BENCHMARK_UNREACHABLE();
+}
+
+inline double GetTimeUnitMultiplier(TimeUnit unit) {
+ switch (unit) {
+ case kSecond:
+ return 1;
+ case kMillisecond:
+ return 1e3;
+ case kMicrosecond:
+ return 1e6;
+ case kNanosecond:
+ return 1e9;
+ }
+ BENCHMARK_UNREACHABLE();
+}
+
+} // namespace benchmark
+
+#endif // BENCHMARK_BENCHMARK_H_
--- /dev/null
+numpy == 1.19.4
+scipy == 1.5.4
--- /dev/null
+import os
+import posixpath
+import re
+import shutil
+import sys
+
+from distutils import sysconfig
+import setuptools
+from setuptools.command import build_ext
+
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+
+
+IS_WINDOWS = sys.platform.startswith("win")
+
+
+def _get_version():
+ """Parse the version string from __init__.py."""
+ with open(
+ os.path.join(HERE, "bindings", "python", "google_benchmark", "__init__.py")
+ ) as init_file:
+ try:
+ version_line = next(
+ line for line in init_file if line.startswith("__version__")
+ )
+ except StopIteration:
+ raise ValueError("__version__ not defined in __init__.py")
+ else:
+ namespace = {}
+ exec(version_line, namespace) # pylint: disable=exec-used
+ return namespace["__version__"]
+
+
+def _parse_requirements(path):
+ with open(os.path.join(HERE, path)) as requirements:
+ return [
+ line.rstrip()
+ for line in requirements
+ if not (line.isspace() or line.startswith("#"))
+ ]
+
+
+class BazelExtension(setuptools.Extension):
+ """A C/C++ extension that is defined as a Bazel BUILD target."""
+
+ def __init__(self, name, bazel_target):
+ self.bazel_target = bazel_target
+ self.relpath, self.target_name = posixpath.relpath(bazel_target, "//").split(
+ ":"
+ )
+ setuptools.Extension.__init__(self, name, sources=[])
+
+
+class BuildBazelExtension(build_ext.build_ext):
+ """A command that runs Bazel to build a C/C++ extension."""
+
+ def run(self):
+ for ext in self.extensions:
+ self.bazel_build(ext)
+ build_ext.build_ext.run(self)
+
+ def bazel_build(self, ext):
+ """Runs the bazel build to create the package."""
+ with open("WORKSPACE", "r") as workspace:
+ workspace_contents = workspace.read()
+
+ with open("WORKSPACE", "w") as workspace:
+ workspace.write(
+ re.sub(
+ r'(?<=path = ").*(?=", # May be overwritten by setup\.py\.)',
+ sysconfig.get_python_inc().replace(os.path.sep, posixpath.sep),
+ workspace_contents,
+ )
+ )
+
+ if not os.path.exists(self.build_temp):
+ os.makedirs(self.build_temp)
+
+ bazel_argv = [
+ "bazel",
+ "build",
+ ext.bazel_target,
+ "--symlink_prefix=" + os.path.join(self.build_temp, "bazel-"),
+ "--compilation_mode=" + ("dbg" if self.debug else "opt"),
+ ]
+
+ if IS_WINDOWS:
+ # Link with python*.lib.
+ for library_dir in self.library_dirs:
+ bazel_argv.append("--linkopt=/LIBPATH:" + library_dir)
+
+ self.spawn(bazel_argv)
+
+ shared_lib_suffix = '.dll' if IS_WINDOWS else '.so'
+ ext_bazel_bin_path = os.path.join(
+ self.build_temp, 'bazel-bin',
+ ext.relpath, ext.target_name + shared_lib_suffix)
+
+ ext_dest_path = self.get_ext_fullpath(ext.name)
+ ext_dest_dir = os.path.dirname(ext_dest_path)
+ if not os.path.exists(ext_dest_dir):
+ os.makedirs(ext_dest_dir)
+ shutil.copyfile(ext_bazel_bin_path, ext_dest_path)
+
+
+setuptools.setup(
+ name="google_benchmark",
+ version=_get_version(),
+ url="https://github.com/google/benchmark",
+ description="A library to benchmark code snippets.",
+ author="Google",
+ author_email="benchmark-py@google.com",
+ # Contained modules and scripts.
+ package_dir={"": "bindings/python"},
+ packages=setuptools.find_packages("bindings/python"),
+ install_requires=_parse_requirements("bindings/python/requirements.txt"),
+ cmdclass=dict(build_ext=BuildBazelExtension),
+ ext_modules=[
+ BazelExtension(
+ "google_benchmark._benchmark",
+ "//bindings/python/google_benchmark:_benchmark",
+ )
+ ],
+ zip_safe=False,
+ # PyPI package information.
+ classifiers=[
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Topic :: Software Development :: Testing",
+ "Topic :: System :: Benchmark",
+ ],
+ license="Apache 2.0",
+ keywords="benchmark",
+)
--- /dev/null
+# Allow the source files to find headers in src/
+include(GNUInstallDirs)
+include_directories(${PROJECT_SOURCE_DIR}/src)
+
+if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
+ list(APPEND CMAKE_SHARED_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
+ list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
+endif()
+
+file(GLOB
+ SOURCE_FILES
+ *.cc
+ ${PROJECT_SOURCE_DIR}/include/benchmark/*.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
+file(GLOB BENCHMARK_MAIN "benchmark_main.cc")
+foreach(item ${BENCHMARK_MAIN})
+ list(REMOVE_ITEM SOURCE_FILES "${item}")
+endforeach()
+
+add_library(benchmark ${SOURCE_FILES})
+add_library(benchmark::benchmark ALIAS benchmark)
+set_target_properties(benchmark PROPERTIES
+ OUTPUT_NAME "benchmark"
+ VERSION ${GENERIC_LIB_VERSION}
+ SOVERSION ${GENERIC_LIB_SOVERSION}
+)
+target_include_directories(benchmark PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
+ )
+
+# libpfm, if available
+if (HAVE_LIBPFM)
+ target_link_libraries(benchmark libpfm.a)
+ add_definitions(-DHAVE_LIBPFM)
+endif()
+
+# Link threads.
+target_link_libraries(benchmark ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+find_library(LIBRT rt)
+if(LIBRT)
+ target_link_libraries(benchmark ${LIBRT})
+endif()
+
+if(CMAKE_BUILD_TYPE)
+ string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
+endif()
+if(NOT CMAKE_THREAD_LIBS_INIT AND "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}" MATCHES ".*-fsanitize=[^ ]*address.*")
+ message(WARNING "CMake's FindThreads.cmake did not fail, but CMAKE_THREAD_LIBS_INIT ended up being empty. This was fixed in https://github.com/Kitware/CMake/commit/d53317130e84898c5328c237186dbd995aaf1c12 Let's guess that -pthread is sufficient.")
+ target_link_libraries(benchmark -pthread)
+endif()
+
+# We need extra libraries on Windows
+if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ target_link_libraries(benchmark shlwapi)
+endif()
+
+# We need extra libraries on Solaris
+if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
+ target_link_libraries(benchmark kstat)
+endif()
+
+# Benchmark main library
+add_library(benchmark_main "benchmark_main.cc")
+add_library(benchmark::benchmark_main ALIAS benchmark_main)
+set_target_properties(benchmark_main PROPERTIES
+ OUTPUT_NAME "benchmark_main"
+ VERSION ${GENERIC_LIB_VERSION}
+ SOVERSION ${GENERIC_LIB_SOVERSION}
+)
+target_include_directories(benchmark PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
+ )
+target_link_libraries(benchmark_main benchmark::benchmark)
+
+
+set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
+
+set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
+set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
+set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
+set(targets_export_name "${PROJECT_NAME}Targets")
+
+set(namespace "${PROJECT_NAME}::")
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ "${version_config}" VERSION ${GENERIC_LIB_VERSION} COMPATIBILITY SameMajorVersion
+)
+
+configure_file("${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in" "${project_config}" @ONLY)
+configure_file("${PROJECT_SOURCE_DIR}/cmake/benchmark.pc.in" "${pkg_config}" @ONLY)
+
+if (BENCHMARK_ENABLE_INSTALL)
+ # Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable)
+ install(
+ TARGETS benchmark benchmark_main
+ EXPORT ${targets_export_name}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+ install(
+ DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark"
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ FILES_MATCHING PATTERN "*.*h")
+
+ install(
+ FILES "${project_config}" "${version_config}"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+ install(
+ FILES "${pkg_config}"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+ install(
+ EXPORT "${targets_export_name}"
+ NAMESPACE "${namespace}"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+endif()
--- /dev/null
+#ifndef BENCHMARK_ARRAYSIZE_H_
+#define BENCHMARK_ARRAYSIZE_H_
+
+#include "internal_macros.h"
+
+namespace benchmark {
+namespace internal {
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef COMPILER_MSVC
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(::benchmark::internal::ArraySizeHelper(array)))
+
+} // end namespace internal
+} // end namespace benchmark
+
+#endif // BENCHMARK_ARRAYSIZE_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+#include "benchmark_api_internal.h"
+#include "benchmark_runner.h"
+#include "internal_macros.h"
+
+#ifndef BENCHMARK_OS_WINDOWS
+#ifndef BENCHMARK_OS_FUCHSIA
+#include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <random>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include "check.h"
+#include "colorprint.h"
+#include "commandlineflags.h"
+#include "complexity.h"
+#include "counter.h"
+#include "internal_macros.h"
+#include "log.h"
+#include "mutex.h"
+#include "perf_counters.h"
+#include "re.h"
+#include "statistics.h"
+#include "string_util.h"
+#include "thread_manager.h"
+#include "thread_timer.h"
+
+// Print a list of benchmarks. This option overrides all other options.
+DEFINE_bool(benchmark_list_tests, false);
+
+// A regular expression that specifies the set of benchmarks to execute. If
+// this flag is empty, or if this flag is the string \"all\", all benchmarks
+// linked into the binary are run.
+DEFINE_string(benchmark_filter, ".");
+
+// Minimum number of seconds we should run benchmark before results are
+// considered significant. For cpu-time based tests, this is the lower bound
+// on the total cpu time used by all threads that make up the test. For
+// real-time based tests, this is the lower bound on the elapsed time of the
+// benchmark execution, regardless of number of threads.
+DEFINE_double(benchmark_min_time, 0.5);
+
+// The number of runs of each benchmark. If greater than 1, the mean and
+// standard deviation of the runs will be reported.
+DEFINE_int32(benchmark_repetitions, 1);
+
+// If set, enable random interleaving of repetitions of all benchmarks.
+// See http://github.com/google/benchmark/issues/1051 for details.
+DEFINE_bool(benchmark_enable_random_interleaving, false);
+
+// Report the result of each benchmark repetitions. When 'true' is specified
+// only the mean, standard deviation, and other statistics are reported for
+// repeated benchmarks. Affects all reporters.
+DEFINE_bool(benchmark_report_aggregates_only, false);
+
+// Display the result of each benchmark repetitions. When 'true' is specified
+// only the mean, standard deviation, and other statistics are displayed for
+// repeated benchmarks. Unlike benchmark_report_aggregates_only, only affects
+// the display reporter, but *NOT* file reporter, which will still contain
+// all the output.
+DEFINE_bool(benchmark_display_aggregates_only, false);
+
+// The format to use for console output.
+// Valid values are 'console', 'json', or 'csv'.
+DEFINE_string(benchmark_format, "console");
+
+// The format to use for file output.
+// Valid values are 'console', 'json', or 'csv'.
+DEFINE_string(benchmark_out_format, "json");
+
+// The file to write additional output to.
+DEFINE_string(benchmark_out, "");
+
+// Whether to use colors in the output. Valid values:
+// 'true'/'yes'/1, 'false'/'no'/0, and 'auto'. 'auto' means to use colors if
+// the output is being sent to a terminal and the TERM environment variable is
+// set to a terminal type that supports colors.
+DEFINE_string(benchmark_color, "auto");
+
+// Whether to use tabular format when printing user counters to the console.
+// Valid values: 'true'/'yes'/1, 'false'/'no'/0. Defaults to false.
+DEFINE_bool(benchmark_counters_tabular, false);
+
+// The level of verbose logging to output
+DEFINE_int32(v, 0);
+
+// List of additional perf counters to collect, in libpfm format. For more
+// information about libpfm: https://man7.org/linux/man-pages/man3/libpfm.3.html
+DEFINE_string(benchmark_perf_counters, "");
+
+namespace benchmark {
+namespace internal {
+
+// Extra context to include in the output formatted as comma-separated key-value
+// pairs. Kept internal as it's only used for parsing from env/command line.
+DEFINE_kvpairs(benchmark_context, {});
+
+std::map<std::string, std::string>* global_context = nullptr;
+
+// FIXME: wouldn't LTO mess this up?
+void UseCharPointer(char const volatile*) {}
+
+} // namespace internal
+
+State::State(IterationCount max_iters, const std::vector<int64_t>& ranges,
+ int thread_i, int n_threads, internal::ThreadTimer* timer,
+ internal::ThreadManager* manager,
+ internal::PerfCountersMeasurement* perf_counters_measurement)
+ : total_iterations_(0),
+ batch_leftover_(0),
+ max_iterations(max_iters),
+ started_(false),
+ finished_(false),
+ error_occurred_(false),
+ range_(ranges),
+ complexity_n_(0),
+ counters(),
+ thread_index(thread_i),
+ threads(n_threads),
+ timer_(timer),
+ manager_(manager),
+ perf_counters_measurement_(perf_counters_measurement) {
+ CHECK(max_iterations != 0) << "At least one iteration must be run";
+ CHECK_LT(thread_index, threads) << "thread_index must be less than threads";
+
+ // Note: The use of offsetof below is technically undefined until C++17
+ // because State is not a standard layout type. However, all compilers
+ // currently provide well-defined behavior as an extension (which is
+ // demonstrated since constexpr evaluation must diagnose all undefined
+ // behavior). However, GCC and Clang also warn about this use of offsetof,
+ // which must be suppressed.
+#if defined(__INTEL_COMPILER)
+#pragma warning push
+#pragma warning(disable : 1875)
+#elif defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
+ // Offset tests to ensure commonly accessed data is on the first cache line.
+ const int cache_line_size = 64;
+ static_assert(offsetof(State, error_occurred_) <=
+ (cache_line_size - sizeof(error_occurred_)),
+ "");
+#if defined(__INTEL_COMPILER)
+#pragma warning pop
+#elif defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+}
+
+void State::PauseTiming() {
+ // Add in time accumulated so far
+ CHECK(started_ && !finished_ && !error_occurred_);
+ timer_->StopTimer();
+ if (perf_counters_measurement_) {
+ auto measurements = perf_counters_measurement_->StopAndGetMeasurements();
+ for (const auto& name_and_measurement : measurements) {
+ auto name = name_and_measurement.first;
+ auto measurement = name_and_measurement.second;
+ CHECK_EQ(counters[name], 0.0);
+ counters[name] = Counter(measurement, Counter::kAvgIterations);
+ }
+ }
+}
+
+void State::ResumeTiming() {
+ CHECK(started_ && !finished_ && !error_occurred_);
+ timer_->StartTimer();
+ if (perf_counters_measurement_) {
+ perf_counters_measurement_->Start();
+ }
+}
+
+void State::SkipWithError(const char* msg) {
+ CHECK(msg);
+ error_occurred_ = true;
+ {
+ MutexLock l(manager_->GetBenchmarkMutex());
+ if (manager_->results.has_error_ == false) {
+ manager_->results.error_message_ = msg;
+ manager_->results.has_error_ = true;
+ }
+ }
+ total_iterations_ = 0;
+ if (timer_->running()) timer_->StopTimer();
+}
+
+void State::SetIterationTime(double seconds) {
+ timer_->SetIterationTime(seconds);
+}
+
+void State::SetLabel(const char* label) {
+ MutexLock l(manager_->GetBenchmarkMutex());
+ manager_->results.report_label_ = label;
+}
+
+void State::StartKeepRunning() {
+ CHECK(!started_ && !finished_);
+ started_ = true;
+ total_iterations_ = error_occurred_ ? 0 : max_iterations;
+ manager_->StartStopBarrier();
+ if (!error_occurred_) ResumeTiming();
+}
+
+void State::FinishKeepRunning() {
+ CHECK(started_ && (!finished_ || error_occurred_));
+ if (!error_occurred_) {
+ PauseTiming();
+ }
+ // Total iterations has now wrapped around past 0. Fix this.
+ total_iterations_ = 0;
+ finished_ = true;
+ manager_->StartStopBarrier();
+}
+
+namespace internal {
+namespace {
+
+// Flushes streams after invoking reporter methods that write to them. This
+// ensures users get timely updates even when streams are not line-buffered.
+void FlushStreams(BenchmarkReporter* reporter) {
+ if (!reporter) return;
+ std::flush(reporter->GetOutputStream());
+ std::flush(reporter->GetErrorStream());
+}
+
+// Reports in both display and file reporters.
+void Report(BenchmarkReporter* display_reporter,
+ BenchmarkReporter* file_reporter, const RunResults& run_results) {
+ auto report_one = [](BenchmarkReporter* reporter, bool aggregates_only,
+ const RunResults& results) {
+ assert(reporter);
+ // If there are no aggregates, do output non-aggregates.
+ aggregates_only &= !results.aggregates_only.empty();
+ if (!aggregates_only) reporter->ReportRuns(results.non_aggregates);
+ if (!results.aggregates_only.empty())
+ reporter->ReportRuns(results.aggregates_only);
+ };
+
+ report_one(display_reporter, run_results.display_report_aggregates_only,
+ run_results);
+ if (file_reporter)
+ report_one(file_reporter, run_results.file_report_aggregates_only,
+ run_results);
+
+ FlushStreams(display_reporter);
+ FlushStreams(file_reporter);
+}
+
+void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
+ BenchmarkReporter* display_reporter,
+ BenchmarkReporter* file_reporter) {
+ // Note the file_reporter can be null.
+ CHECK(display_reporter != nullptr);
+
+ // Determine the width of the name field using a minimum width of 10.
+ bool might_have_aggregates = FLAGS_benchmark_repetitions > 1;
+ size_t name_field_width = 10;
+ size_t stat_field_width = 0;
+ for (const BenchmarkInstance& benchmark : benchmarks) {
+ name_field_width =
+ std::max<size_t>(name_field_width, benchmark.name().str().size());
+ might_have_aggregates |= benchmark.repetitions() > 1;
+
+ for (const auto& Stat : benchmark.statistics())
+ stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size());
+ }
+ if (might_have_aggregates) name_field_width += 1 + stat_field_width;
+
+ // Print header here
+ BenchmarkReporter::Context context;
+ context.name_field_width = name_field_width;
+
+ // Keep track of running times of all instances of each benchmark family.
+ std::map<int /*family_index*/, BenchmarkReporter::PerFamilyRunReports>
+ per_family_reports;
+
+ if (display_reporter->ReportContext(context) &&
+ (!file_reporter || file_reporter->ReportContext(context))) {
+ FlushStreams(display_reporter);
+ FlushStreams(file_reporter);
+
+ size_t num_repetitions_total = 0;
+
+ std::vector<internal::BenchmarkRunner> runners;
+ runners.reserve(benchmarks.size());
+ for (const BenchmarkInstance& benchmark : benchmarks) {
+ BenchmarkReporter::PerFamilyRunReports* reports_for_family = nullptr;
+ if (benchmark.complexity() != oNone)
+ reports_for_family = &per_family_reports[benchmark.family_index()];
+
+ runners.emplace_back(benchmark, reports_for_family);
+ int num_repeats_of_this_instance = runners.back().GetNumRepeats();
+ num_repetitions_total += num_repeats_of_this_instance;
+ if (reports_for_family)
+ reports_for_family->num_runs_total += num_repeats_of_this_instance;
+ }
+ assert(runners.size() == benchmarks.size() && "Unexpected runner count.");
+
+ std::vector<int> repetition_indices;
+ repetition_indices.reserve(num_repetitions_total);
+ for (size_t runner_index = 0, num_runners = runners.size();
+ runner_index != num_runners; ++runner_index) {
+ const internal::BenchmarkRunner& runner = runners[runner_index];
+ std::fill_n(std::back_inserter(repetition_indices),
+ runner.GetNumRepeats(), runner_index);
+ }
+ assert(repetition_indices.size() == num_repetitions_total &&
+ "Unexpected number of repetition indexes.");
+
+ if (FLAGS_benchmark_enable_random_interleaving) {
+ std::random_device rd;
+ std::mt19937 g(rd());
+ std::shuffle(repetition_indices.begin(), repetition_indices.end(), g);
+ }
+
+ for (size_t repetition_index : repetition_indices) {
+ internal::BenchmarkRunner& runner = runners[repetition_index];
+ runner.DoOneRepetition();
+ if (runner.HasRepeatsRemaining()) continue;
+ // FIXME: report each repetition separately, not all of them in bulk.
+
+ RunResults run_results = runner.GetResults();
+
+ // Maybe calculate complexity report
+ if (const auto* reports_for_family = runner.GetReportsForFamily()) {
+ if (reports_for_family->num_runs_done ==
+ reports_for_family->num_runs_total) {
+ auto additional_run_stats = ComputeBigO(reports_for_family->Runs);
+ run_results.aggregates_only.insert(run_results.aggregates_only.end(),
+ additional_run_stats.begin(),
+ additional_run_stats.end());
+ per_family_reports.erase(
+ (int)reports_for_family->Runs.front().family_index);
+ }
+ }
+
+ Report(display_reporter, file_reporter, run_results);
+ }
+ }
+ display_reporter->Finalize();
+ if (file_reporter) file_reporter->Finalize();
+ FlushStreams(display_reporter);
+ FlushStreams(file_reporter);
+}
+
+// Disable deprecated warnings temporarily because we need to reference
+// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+std::unique_ptr<BenchmarkReporter> CreateReporter(
+ std::string const& name, ConsoleReporter::OutputOptions output_opts) {
+ typedef std::unique_ptr<BenchmarkReporter> PtrType;
+ if (name == "console") {
+ return PtrType(new ConsoleReporter(output_opts));
+ } else if (name == "json") {
+ return PtrType(new JSONReporter);
+ } else if (name == "csv") {
+ return PtrType(new CSVReporter);
+ } else {
+ std::cerr << "Unexpected format: '" << name << "'\n";
+ std::exit(1);
+ }
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+} // end namespace
+
+bool IsZero(double n) {
+ return std::abs(n) < std::numeric_limits<double>::epsilon();
+}
+
+ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color) {
+ int output_opts = ConsoleReporter::OO_Defaults;
+ auto is_benchmark_color = [force_no_color]() -> bool {
+ if (force_no_color) {
+ return false;
+ }
+ if (FLAGS_benchmark_color == "auto") {
+ return IsColorTerminal();
+ }
+ return IsTruthyFlagValue(FLAGS_benchmark_color);
+ };
+ if (is_benchmark_color()) {
+ output_opts |= ConsoleReporter::OO_Color;
+ } else {
+ output_opts &= ~ConsoleReporter::OO_Color;
+ }
+ if (FLAGS_benchmark_counters_tabular) {
+ output_opts |= ConsoleReporter::OO_Tabular;
+ } else {
+ output_opts &= ~ConsoleReporter::OO_Tabular;
+ }
+ return static_cast<ConsoleReporter::OutputOptions>(output_opts);
+}
+
+} // end namespace internal
+
+size_t RunSpecifiedBenchmarks() {
+ return RunSpecifiedBenchmarks(nullptr, nullptr);
+}
+
+size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter) {
+ return RunSpecifiedBenchmarks(display_reporter, nullptr);
+}
+
+size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
+ BenchmarkReporter* file_reporter) {
+ std::string spec = FLAGS_benchmark_filter;
+ if (spec.empty() || spec == "all")
+ spec = "."; // Regexp that matches all benchmarks
+
+ // Setup the reporters
+ std::ofstream output_file;
+ std::unique_ptr<BenchmarkReporter> default_display_reporter;
+ std::unique_ptr<BenchmarkReporter> default_file_reporter;
+ if (!display_reporter) {
+ default_display_reporter = internal::CreateReporter(
+ FLAGS_benchmark_format, internal::GetOutputOptions());
+ display_reporter = default_display_reporter.get();
+ }
+ auto& Out = display_reporter->GetOutputStream();
+ auto& Err = display_reporter->GetErrorStream();
+
+ std::string const& fname = FLAGS_benchmark_out;
+ if (fname.empty() && file_reporter) {
+ Err << "A custom file reporter was provided but "
+ "--benchmark_out=<file> was not specified."
+ << std::endl;
+ std::exit(1);
+ }
+ if (!fname.empty()) {
+ output_file.open(fname);
+ if (!output_file.is_open()) {
+ Err << "invalid file name: '" << fname << "'" << std::endl;
+ std::exit(1);
+ }
+ if (!file_reporter) {
+ default_file_reporter = internal::CreateReporter(
+ FLAGS_benchmark_out_format, ConsoleReporter::OO_None);
+ file_reporter = default_file_reporter.get();
+ }
+ file_reporter->SetOutputStream(&output_file);
+ file_reporter->SetErrorStream(&output_file);
+ }
+
+ std::vector<internal::BenchmarkInstance> benchmarks;
+ if (!FindBenchmarksInternal(spec, &benchmarks, &Err)) return 0;
+
+ if (benchmarks.empty()) {
+ Err << "Failed to match any benchmarks against regex: " << spec << "\n";
+ return 0;
+ }
+
+ if (FLAGS_benchmark_list_tests) {
+ for (auto const& benchmark : benchmarks)
+ Out << benchmark.name().str() << "\n";
+ } else {
+ internal::RunBenchmarks(benchmarks, display_reporter, file_reporter);
+ }
+
+ return benchmarks.size();
+}
+
+void RegisterMemoryManager(MemoryManager* manager) {
+ internal::memory_manager = manager;
+}
+
+void AddCustomContext(const std::string& key, const std::string& value) {
+ if (internal::global_context == nullptr) {
+ internal::global_context = new std::map<std::string, std::string>();
+ }
+ if (!internal::global_context->emplace(key, value).second) {
+ std::cerr << "Failed to add custom context \"" << key << "\" as it already "
+ << "exists with value \"" << value << "\"\n";
+ }
+}
+
+namespace internal {
+
+void PrintUsageAndExit() {
+ fprintf(stdout,
+ "benchmark"
+ " [--benchmark_list_tests={true|false}]\n"
+ " [--benchmark_filter=<regex>]\n"
+ " [--benchmark_min_time=<min_time>]\n"
+ " [--benchmark_repetitions=<num_repetitions>]\n"
+ " [--benchmark_enable_random_interleaving={true|false}]\n"
+ " [--benchmark_report_aggregates_only={true|false}]\n"
+ " [--benchmark_display_aggregates_only={true|false}]\n"
+ " [--benchmark_format=<console|json|csv>]\n"
+ " [--benchmark_out=<filename>]\n"
+ " [--benchmark_out_format=<json|console|csv>]\n"
+ " [--benchmark_color={auto|true|false}]\n"
+ " [--benchmark_counters_tabular={true|false}]\n"
+ " [--benchmark_context=<key>=<value>,...]\n"
+ " [--v=<verbosity>]\n");
+ exit(0);
+}
+
+void ParseCommandLineFlags(int* argc, char** argv) {
+ using namespace benchmark;
+ BenchmarkReporter::Context::executable_name =
+ (argc && *argc > 0) ? argv[0] : "unknown";
+ for (int i = 1; argc && i < *argc; ++i) {
+ if (ParseBoolFlag(argv[i], "benchmark_list_tests",
+ &FLAGS_benchmark_list_tests) ||
+ ParseStringFlag(argv[i], "benchmark_filter", &FLAGS_benchmark_filter) ||
+ ParseDoubleFlag(argv[i], "benchmark_min_time",
+ &FLAGS_benchmark_min_time) ||
+ ParseInt32Flag(argv[i], "benchmark_repetitions",
+ &FLAGS_benchmark_repetitions) ||
+ ParseBoolFlag(argv[i], "benchmark_enable_random_interleaving",
+ &FLAGS_benchmark_enable_random_interleaving) ||
+ ParseBoolFlag(argv[i], "benchmark_report_aggregates_only",
+ &FLAGS_benchmark_report_aggregates_only) ||
+ ParseBoolFlag(argv[i], "benchmark_display_aggregates_only",
+ &FLAGS_benchmark_display_aggregates_only) ||
+ ParseStringFlag(argv[i], "benchmark_format", &FLAGS_benchmark_format) ||
+ ParseStringFlag(argv[i], "benchmark_out", &FLAGS_benchmark_out) ||
+ ParseStringFlag(argv[i], "benchmark_out_format",
+ &FLAGS_benchmark_out_format) ||
+ ParseStringFlag(argv[i], "benchmark_color", &FLAGS_benchmark_color) ||
+ // "color_print" is the deprecated name for "benchmark_color".
+ // TODO: Remove this.
+ ParseStringFlag(argv[i], "color_print", &FLAGS_benchmark_color) ||
+ ParseBoolFlag(argv[i], "benchmark_counters_tabular",
+ &FLAGS_benchmark_counters_tabular) ||
+ ParseStringFlag(argv[i], "benchmark_perf_counters",
+ &FLAGS_benchmark_perf_counters) ||
+ ParseKeyValueFlag(argv[i], "benchmark_context",
+ &FLAGS_benchmark_context) ||
+ ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
+ for (int j = i; j != *argc - 1; ++j) argv[j] = argv[j + 1];
+
+ --(*argc);
+ --i;
+ } else if (IsFlag(argv[i], "help")) {
+ PrintUsageAndExit();
+ }
+ }
+ for (auto const* flag :
+ {&FLAGS_benchmark_format, &FLAGS_benchmark_out_format}) {
+ if (*flag != "console" && *flag != "json" && *flag != "csv") {
+ PrintUsageAndExit();
+ }
+ }
+ if (FLAGS_benchmark_color.empty()) {
+ PrintUsageAndExit();
+ }
+ for (const auto& kv : FLAGS_benchmark_context) {
+ AddCustomContext(kv.first, kv.second);
+ }
+}
+
+int InitializeStreams() {
+ static std::ios_base::Init init;
+ return 0;
+}
+
+} // end namespace internal
+
+void Initialize(int* argc, char** argv) {
+ internal::ParseCommandLineFlags(argc, argv);
+ internal::LogLevel() = FLAGS_v;
+}
+
+void Shutdown() {
+ delete internal::global_context;
+}
+
+bool ReportUnrecognizedArguments(int argc, char** argv) {
+ for (int i = 1; i < argc; ++i) {
+ fprintf(stderr, "%s: error: unrecognized command-line flag: %s\n", argv[0],
+ argv[i]);
+ }
+ return argc > 1;
+}
+
+} // end namespace benchmark
--- /dev/null
+#include "benchmark_api_internal.h"
+
+#include <cinttypes>
+
+#include "string_util.h"
+
+namespace benchmark {
+namespace internal {
+
+BenchmarkInstance::BenchmarkInstance(Benchmark* benchmark, int family_idx,
+ int per_family_instance_idx,
+ const std::vector<int64_t>& args,
+ int thread_count)
+ : benchmark_(*benchmark),
+ family_index_(family_idx),
+ per_family_instance_index_(per_family_instance_idx),
+ aggregation_report_mode_(benchmark_.aggregation_report_mode_),
+ args_(args),
+ time_unit_(benchmark_.time_unit_),
+ measure_process_cpu_time_(benchmark_.measure_process_cpu_time_),
+ use_real_time_(benchmark_.use_real_time_),
+ use_manual_time_(benchmark_.use_manual_time_),
+ complexity_(benchmark_.complexity_),
+ complexity_lambda_(benchmark_.complexity_lambda_),
+ statistics_(benchmark_.statistics_),
+ repetitions_(benchmark_.repetitions_),
+ min_time_(benchmark_.min_time_),
+ iterations_(benchmark_.iterations_),
+ threads_(thread_count) {
+ name_.function_name = benchmark_.name_;
+
+ size_t arg_i = 0;
+ for (const auto& arg : args) {
+ if (!name_.args.empty()) {
+ name_.args += '/';
+ }
+
+ if (arg_i < benchmark->arg_names_.size()) {
+ const auto& arg_name = benchmark_.arg_names_[arg_i];
+ if (!arg_name.empty()) {
+ name_.args += StrFormat("%s:", arg_name.c_str());
+ }
+ }
+
+ name_.args += StrFormat("%" PRId64, arg);
+ ++arg_i;
+ }
+
+ if (!IsZero(benchmark->min_time_)) {
+ name_.min_time = StrFormat("min_time:%0.3f", benchmark_.min_time_);
+ }
+
+ if (benchmark_.iterations_ != 0) {
+ name_.iterations = StrFormat(
+ "iterations:%lu", static_cast<unsigned long>(benchmark_.iterations_));
+ }
+
+ if (benchmark_.repetitions_ != 0) {
+ name_.repetitions = StrFormat("repeats:%d", benchmark_.repetitions_);
+ }
+
+ if (benchmark_.measure_process_cpu_time_) {
+ name_.time_type = "process_time";
+ }
+
+ if (benchmark_.use_manual_time_) {
+ if (!name_.time_type.empty()) {
+ name_.time_type += '/';
+ }
+ name_.time_type += "manual_time";
+ } else if (benchmark_.use_real_time_) {
+ if (!name_.time_type.empty()) {
+ name_.time_type += '/';
+ }
+ name_.time_type += "real_time";
+ }
+
+ if (!benchmark_.thread_counts_.empty()) {
+ name_.threads = StrFormat("threads:%d", threads_);
+ }
+}
+
+State BenchmarkInstance::Run(
+ IterationCount iters, int thread_id, internal::ThreadTimer* timer,
+ internal::ThreadManager* manager,
+ internal::PerfCountersMeasurement* perf_counters_measurement) const {
+ State st(iters, args_, thread_id, threads_, timer, manager,
+ perf_counters_measurement);
+ benchmark_.Run(st);
+ return st;
+}
+
+} // namespace internal
+} // namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_API_INTERNAL_H
+#define BENCHMARK_API_INTERNAL_H
+
+#include <cmath>
+#include <iosfwd>
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "commandlineflags.h"
+
+namespace benchmark {
+namespace internal {
+
+// Information kept per benchmark we may want to run
+class BenchmarkInstance {
+ public:
+ BenchmarkInstance(Benchmark* benchmark, int family_index,
+ int per_family_instance_index,
+ const std::vector<int64_t>& args, int threads);
+
+ const BenchmarkName& name() const { return name_; }
+ int family_index() const { return family_index_; }
+ int per_family_instance_index() const { return per_family_instance_index_; }
+ AggregationReportMode aggregation_report_mode() const {
+ return aggregation_report_mode_;
+ }
+ TimeUnit time_unit() const { return time_unit_; }
+ bool measure_process_cpu_time() const { return measure_process_cpu_time_; }
+ bool use_real_time() const { return use_real_time_; }
+ bool use_manual_time() const { return use_manual_time_; }
+ BigO complexity() const { return complexity_; }
+ BigOFunc& complexity_lambda() const { return *complexity_lambda_; }
+ const std::vector<Statistics>& statistics() const { return statistics_; }
+ int repetitions() const { return repetitions_; }
+ double min_time() const { return min_time_; }
+ IterationCount iterations() const { return iterations_; }
+ int threads() const { return threads_; }
+
+ State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer,
+ internal::ThreadManager* manager,
+ internal::PerfCountersMeasurement* perf_counters_measurement) const;
+
+ private:
+ BenchmarkName name_;
+ Benchmark& benchmark_;
+ const int family_index_;
+ const int per_family_instance_index_;
+ AggregationReportMode aggregation_report_mode_;
+ const std::vector<int64_t>& args_;
+ TimeUnit time_unit_;
+ bool measure_process_cpu_time_;
+ bool use_real_time_;
+ bool use_manual_time_;
+ BigO complexity_;
+ BigOFunc* complexity_lambda_;
+ UserCounters counters_;
+ const std::vector<Statistics>& statistics_;
+ int repetitions_;
+ double min_time_;
+ IterationCount iterations_;
+ int threads_; // Number of concurrent threads to us
+};
+
+bool FindBenchmarksInternal(const std::string& re,
+ std::vector<BenchmarkInstance>* benchmarks,
+ std::ostream* Err);
+
+bool IsZero(double n);
+
+ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false);
+
+} // end namespace internal
+} // end namespace benchmark
+
+#endif // BENCHMARK_API_INTERNAL_H
--- /dev/null
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+BENCHMARK_MAIN();
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <benchmark/benchmark.h>
+
+namespace benchmark {
+
+namespace {
+
+// Compute the total size of a pack of std::strings
+size_t size_impl() { return 0; }
+
+template <typename Head, typename... Tail>
+size_t size_impl(const Head& head, const Tail&... tail) {
+ return head.size() + size_impl(tail...);
+}
+
+// Join a pack of std::strings using a delimiter
+// TODO: use absl::StrJoin
+void join_impl(std::string&, char) {}
+
+template <typename Head, typename... Tail>
+void join_impl(std::string& s, const char delimiter, const Head& head,
+ const Tail&... tail) {
+ if (!s.empty() && !head.empty()) {
+ s += delimiter;
+ }
+
+ s += head;
+
+ join_impl(s, delimiter, tail...);
+}
+
+template <typename... Ts>
+std::string join(char delimiter, const Ts&... ts) {
+ std::string s;
+ s.reserve(sizeof...(Ts) + size_impl(ts...));
+ join_impl(s, delimiter, ts...);
+ return s;
+}
+} // namespace
+
+std::string BenchmarkName::str() const {
+ return join('/', function_name, args, min_time, iterations, repetitions,
+ time_type, threads);
+}
+} // namespace benchmark
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark_register.h"
+
+#ifndef BENCHMARK_OS_WINDOWS
+#ifndef BENCHMARK_OS_FUCHSIA
+#include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <cinttypes>
+#include <condition_variable>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <sstream>
+#include <thread>
+
+#include "benchmark/benchmark.h"
+#include "benchmark_api_internal.h"
+#include "check.h"
+#include "commandlineflags.h"
+#include "complexity.h"
+#include "internal_macros.h"
+#include "log.h"
+#include "mutex.h"
+#include "re.h"
+#include "statistics.h"
+#include "string_util.h"
+#include "timers.h"
+
+namespace benchmark {
+
+namespace {
+// For non-dense Range, intermediate values are powers of kRangeMultiplier.
+static const int kRangeMultiplier = 8;
+// The size of a benchmark family determines is the number of inputs to repeat
+// the benchmark on. If this is "large" then warn the user during configuration.
+static const size_t kMaxFamilySize = 100;
+} // end namespace
+
+namespace internal {
+
+//=============================================================================//
+// BenchmarkFamilies
+//=============================================================================//
+
+// Class for managing registered benchmarks. Note that each registered
+// benchmark identifies a family of related benchmarks to run.
+class BenchmarkFamilies {
+ public:
+ static BenchmarkFamilies* GetInstance();
+
+ // Registers a benchmark family and returns the index assigned to it.
+ size_t AddBenchmark(std::unique_ptr<Benchmark> family);
+
+ // Clear all registered benchmark families.
+ void ClearBenchmarks();
+
+ // Extract the list of benchmark instances that match the specified
+ // regular expression.
+ bool FindBenchmarks(std::string re,
+ std::vector<BenchmarkInstance>* benchmarks,
+ std::ostream* Err);
+
+ private:
+ BenchmarkFamilies() {}
+
+ std::vector<std::unique_ptr<Benchmark>> families_;
+ Mutex mutex_;
+};
+
+BenchmarkFamilies* BenchmarkFamilies::GetInstance() {
+ static BenchmarkFamilies instance;
+ return &instance;
+}
+
+size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr<Benchmark> family) {
+ MutexLock l(mutex_);
+ size_t index = families_.size();
+ families_.push_back(std::move(family));
+ return index;
+}
+
+void BenchmarkFamilies::ClearBenchmarks() {
+ MutexLock l(mutex_);
+ families_.clear();
+ families_.shrink_to_fit();
+}
+
+bool BenchmarkFamilies::FindBenchmarks(
+ std::string spec, std::vector<BenchmarkInstance>* benchmarks,
+ std::ostream* ErrStream) {
+ CHECK(ErrStream);
+ auto& Err = *ErrStream;
+ // Make regular expression out of command-line flag
+ std::string error_msg;
+ Regex re;
+ bool isNegativeFilter = false;
+ if (spec[0] == '-') {
+ spec.replace(0, 1, "");
+ isNegativeFilter = true;
+ }
+ if (!re.Init(spec, &error_msg)) {
+ Err << "Could not compile benchmark re: " << error_msg << std::endl;
+ return false;
+ }
+
+ // Special list of thread counts to use when none are specified
+ const std::vector<int> one_thread = {1};
+
+ int next_family_index = 0;
+
+ MutexLock l(mutex_);
+ for (std::unique_ptr<Benchmark>& family : families_) {
+ int family_index = next_family_index;
+ int per_family_instance_index = 0;
+
+ // Family was deleted or benchmark doesn't match
+ if (!family) continue;
+
+ if (family->ArgsCnt() == -1) {
+ family->Args({});
+ }
+ const std::vector<int>* thread_counts =
+ (family->thread_counts_.empty()
+ ? &one_thread
+ : &static_cast<const std::vector<int>&>(family->thread_counts_));
+ const size_t family_size = family->args_.size() * thread_counts->size();
+ // The benchmark will be run at least 'family_size' different inputs.
+ // If 'family_size' is very large warn the user.
+ if (family_size > kMaxFamilySize) {
+ Err << "The number of inputs is very large. " << family->name_
+ << " will be repeated at least " << family_size << " times.\n";
+ }
+ // reserve in the special case the regex ".", since we know the final
+ // family size.
+ if (spec == ".") benchmarks->reserve(benchmarks->size() + family_size);
+
+ for (auto const& args : family->args_) {
+ for (int num_threads : *thread_counts) {
+ BenchmarkInstance instance(family.get(), family_index,
+ per_family_instance_index, args,
+ num_threads);
+
+ const auto full_name = instance.name().str();
+ if ((re.Match(full_name) && !isNegativeFilter) ||
+ (!re.Match(full_name) && isNegativeFilter)) {
+ benchmarks->push_back(std::move(instance));
+
+ ++per_family_instance_index;
+
+ // Only bump the next family index once we've estabilished that
+ // at least one instance of this family will be run.
+ if (next_family_index == family_index) ++next_family_index;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Benchmark* RegisterBenchmarkInternal(Benchmark* bench) {
+ std::unique_ptr<Benchmark> bench_ptr(bench);
+ BenchmarkFamilies* families = BenchmarkFamilies::GetInstance();
+ families->AddBenchmark(std::move(bench_ptr));
+ return bench;
+}
+
+// FIXME: This function is a hack so that benchmark.cc can access
+// `BenchmarkFamilies`
+bool FindBenchmarksInternal(const std::string& re,
+ std::vector<BenchmarkInstance>* benchmarks,
+ std::ostream* Err) {
+ return BenchmarkFamilies::GetInstance()->FindBenchmarks(re, benchmarks, Err);
+}
+
+//=============================================================================//
+// Benchmark
+//=============================================================================//
+
+Benchmark::Benchmark(const char* name)
+ : name_(name),
+ aggregation_report_mode_(ARM_Unspecified),
+ time_unit_(kNanosecond),
+ range_multiplier_(kRangeMultiplier),
+ min_time_(0),
+ iterations_(0),
+ repetitions_(0),
+ measure_process_cpu_time_(false),
+ use_real_time_(false),
+ use_manual_time_(false),
+ complexity_(oNone),
+ complexity_lambda_(nullptr) {
+ ComputeStatistics("mean", StatisticsMean);
+ ComputeStatistics("median", StatisticsMedian);
+ ComputeStatistics("stddev", StatisticsStdDev);
+}
+
+Benchmark::~Benchmark() {}
+
+Benchmark* Benchmark::Name(const std::string& name) {
+ SetName(name.c_str());
+ return this;
+}
+
+Benchmark* Benchmark::Arg(int64_t x) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
+ args_.push_back({x});
+ return this;
+}
+
+Benchmark* Benchmark::Unit(TimeUnit unit) {
+ time_unit_ = unit;
+ return this;
+}
+
+Benchmark* Benchmark::Range(int64_t start, int64_t limit) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
+ std::vector<int64_t> arglist;
+ AddRange(&arglist, start, limit, range_multiplier_);
+
+ for (int64_t i : arglist) {
+ args_.push_back({i});
+ }
+ return this;
+}
+
+Benchmark* Benchmark::Ranges(
+ const std::vector<std::pair<int64_t, int64_t>>& ranges) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(ranges.size()));
+ std::vector<std::vector<int64_t>> arglists(ranges.size());
+ for (std::size_t i = 0; i < ranges.size(); i++) {
+ AddRange(&arglists[i], ranges[i].first, ranges[i].second,
+ range_multiplier_);
+ }
+
+ ArgsProduct(arglists);
+
+ return this;
+}
+
+Benchmark* Benchmark::ArgsProduct(
+ const std::vector<std::vector<int64_t>>& arglists) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(arglists.size()));
+
+ std::vector<std::size_t> indices(arglists.size());
+ const std::size_t total = std::accumulate(
+ std::begin(arglists), std::end(arglists), std::size_t{1},
+ [](const std::size_t res, const std::vector<int64_t>& arglist) {
+ return res * arglist.size();
+ });
+ std::vector<int64_t> args;
+ args.reserve(arglists.size());
+ for (std::size_t i = 0; i < total; i++) {
+ for (std::size_t arg = 0; arg < arglists.size(); arg++) {
+ args.push_back(arglists[arg][indices[arg]]);
+ }
+ args_.push_back(args);
+ args.clear();
+
+ std::size_t arg = 0;
+ do {
+ indices[arg] = (indices[arg] + 1) % arglists[arg].size();
+ } while (indices[arg++] == 0 && arg < arglists.size());
+ }
+
+ return this;
+}
+
+Benchmark* Benchmark::ArgName(const std::string& name) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
+ arg_names_ = {name};
+ return this;
+}
+
+Benchmark* Benchmark::ArgNames(const std::vector<std::string>& names) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(names.size()));
+ arg_names_ = names;
+ return this;
+}
+
+Benchmark* Benchmark::DenseRange(int64_t start, int64_t limit, int step) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
+ CHECK_LE(start, limit);
+ for (int64_t arg = start; arg <= limit; arg += step) {
+ args_.push_back({arg});
+ }
+ return this;
+}
+
+Benchmark* Benchmark::Args(const std::vector<int64_t>& args) {
+ CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(args.size()));
+ args_.push_back(args);
+ return this;
+}
+
+Benchmark* Benchmark::Apply(void (*custom_arguments)(Benchmark* benchmark)) {
+ custom_arguments(this);
+ return this;
+}
+
+Benchmark* Benchmark::RangeMultiplier(int multiplier) {
+ CHECK(multiplier > 1);
+ range_multiplier_ = multiplier;
+ return this;
+}
+
+Benchmark* Benchmark::MinTime(double t) {
+ CHECK(t > 0.0);
+ CHECK(iterations_ == 0);
+ min_time_ = t;
+ return this;
+}
+
+Benchmark* Benchmark::Iterations(IterationCount n) {
+ CHECK(n > 0);
+ CHECK(IsZero(min_time_));
+ iterations_ = n;
+ return this;
+}
+
+Benchmark* Benchmark::Repetitions(int n) {
+ CHECK(n > 0);
+ repetitions_ = n;
+ return this;
+}
+
+Benchmark* Benchmark::ReportAggregatesOnly(bool value) {
+ aggregation_report_mode_ = value ? ARM_ReportAggregatesOnly : ARM_Default;
+ return this;
+}
+
+Benchmark* Benchmark::DisplayAggregatesOnly(bool value) {
+ // If we were called, the report mode is no longer 'unspecified', in any case.
+ aggregation_report_mode_ = static_cast<AggregationReportMode>(
+ aggregation_report_mode_ | ARM_Default);
+
+ if (value) {
+ aggregation_report_mode_ = static_cast<AggregationReportMode>(
+ aggregation_report_mode_ | ARM_DisplayReportAggregatesOnly);
+ } else {
+ aggregation_report_mode_ = static_cast<AggregationReportMode>(
+ aggregation_report_mode_ & ~ARM_DisplayReportAggregatesOnly);
+ }
+
+ return this;
+}
+
+Benchmark* Benchmark::MeasureProcessCPUTime() {
+ // Can be used together with UseRealTime() / UseManualTime().
+ measure_process_cpu_time_ = true;
+ return this;
+}
+
+Benchmark* Benchmark::UseRealTime() {
+ CHECK(!use_manual_time_)
+ << "Cannot set UseRealTime and UseManualTime simultaneously.";
+ use_real_time_ = true;
+ return this;
+}
+
+Benchmark* Benchmark::UseManualTime() {
+ CHECK(!use_real_time_)
+ << "Cannot set UseRealTime and UseManualTime simultaneously.";
+ use_manual_time_ = true;
+ return this;
+}
+
+Benchmark* Benchmark::Complexity(BigO complexity) {
+ complexity_ = complexity;
+ return this;
+}
+
+Benchmark* Benchmark::Complexity(BigOFunc* complexity) {
+ complexity_lambda_ = complexity;
+ complexity_ = oLambda;
+ return this;
+}
+
+Benchmark* Benchmark::ComputeStatistics(std::string name,
+ StatisticsFunc* statistics) {
+ statistics_.emplace_back(name, statistics);
+ return this;
+}
+
+Benchmark* Benchmark::Threads(int t) {
+ CHECK_GT(t, 0);
+ thread_counts_.push_back(t);
+ return this;
+}
+
+Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) {
+ CHECK_GT(min_threads, 0);
+ CHECK_GE(max_threads, min_threads);
+
+ AddRange(&thread_counts_, min_threads, max_threads, 2);
+ return this;
+}
+
+Benchmark* Benchmark::DenseThreadRange(int min_threads, int max_threads,
+ int stride) {
+ CHECK_GT(min_threads, 0);
+ CHECK_GE(max_threads, min_threads);
+ CHECK_GE(stride, 1);
+
+ for (auto i = min_threads; i < max_threads; i += stride) {
+ thread_counts_.push_back(i);
+ }
+ thread_counts_.push_back(max_threads);
+ return this;
+}
+
+Benchmark* Benchmark::ThreadPerCpu() {
+ thread_counts_.push_back(CPUInfo::Get().num_cpus);
+ return this;
+}
+
+void Benchmark::SetName(const char* name) { name_ = name; }
+
+int Benchmark::ArgsCnt() const {
+ if (args_.empty()) {
+ if (arg_names_.empty()) return -1;
+ return static_cast<int>(arg_names_.size());
+ }
+ return static_cast<int>(args_.front().size());
+}
+
+//=============================================================================//
+// FunctionBenchmark
+//=============================================================================//
+
+void FunctionBenchmark::Run(State& st) { func_(st); }
+
+} // end namespace internal
+
+void ClearRegisteredBenchmarks() {
+ internal::BenchmarkFamilies::GetInstance()->ClearBenchmarks();
+}
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_REGISTER_H
+#define BENCHMARK_REGISTER_H
+
+#include <limits>
+#include <vector>
+
+#include "check.h"
+
+namespace benchmark {
+namespace internal {
+
+// Append the powers of 'mult' in the closed interval [lo, hi].
+// Returns iterator to the start of the inserted range.
+template <typename T>
+typename std::vector<T>::iterator
+AddPowers(std::vector<T>* dst, T lo, T hi, int mult) {
+ CHECK_GE(lo, 0);
+ CHECK_GE(hi, lo);
+ CHECK_GE(mult, 2);
+
+ const size_t start_offset = dst->size();
+
+ static const T kmax = std::numeric_limits<T>::max();
+
+ // Space out the values in multiples of "mult"
+ for (T i = static_cast<T>(1); i <= hi; i *= mult) {
+ if (i >= lo) {
+ dst->push_back(i);
+ }
+ // Break the loop here since multiplying by
+ // 'mult' would move outside of the range of T
+ if (i > kmax / mult) break;
+ }
+
+ return dst->begin() + start_offset;
+}
+
+template <typename T>
+void AddNegatedPowers(std::vector<T>* dst, T lo, T hi, int mult) {
+ // We negate lo and hi so we require that they cannot be equal to 'min'.
+ CHECK_GT(lo, std::numeric_limits<T>::min());
+ CHECK_GT(hi, std::numeric_limits<T>::min());
+ CHECK_GE(hi, lo);
+ CHECK_LE(hi, 0);
+
+ // Add positive powers, then negate and reverse.
+ // Casts necessary since small integers get promoted
+ // to 'int' when negating.
+ const auto lo_complement = static_cast<T>(-lo);
+ const auto hi_complement = static_cast<T>(-hi);
+
+ const auto it = AddPowers(dst, hi_complement, lo_complement, mult);
+
+ std::for_each(it, dst->end(), [](T& t) { t *= -1; });
+ std::reverse(it, dst->end());
+}
+
+template <typename T>
+void AddRange(std::vector<T>* dst, T lo, T hi, int mult) {
+ static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
+ "Args type must be a signed integer");
+
+ CHECK_GE(hi, lo);
+ CHECK_GE(mult, 2);
+
+ // Add "lo"
+ dst->push_back(lo);
+
+ // Handle lo == hi as a special case, so we then know
+ // lo < hi and so it is safe to add 1 to lo and subtract 1
+ // from hi without falling outside of the range of T.
+ if (lo == hi) return;
+
+ // Ensure that lo_inner <= hi_inner below.
+ if (lo + 1 == hi) {
+ dst->push_back(hi);
+ return;
+ }
+
+ // Add all powers of 'mult' in the range [lo+1, hi-1] (inclusive).
+ const auto lo_inner = static_cast<T>(lo + 1);
+ const auto hi_inner = static_cast<T>(hi - 1);
+
+ // Insert negative values
+ if (lo_inner < 0) {
+ AddNegatedPowers(dst, lo_inner, std::min(hi_inner, T{-1}), mult);
+ }
+
+ // Treat 0 as a special case (see discussion on #762).
+ if (lo < 0 && hi >= 0) {
+ dst->push_back(0);
+ }
+
+ // Insert positive values
+ if (hi_inner > 0) {
+ AddPowers(dst, std::max(lo_inner, T{1}), hi_inner, mult);
+ }
+
+ // Add "hi" (if different from last value).
+ if (hi != dst->back()) {
+ dst->push_back(hi);
+ }
+}
+
+} // namespace internal
+} // namespace benchmark
+
+#endif // BENCHMARK_REGISTER_H
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark_runner.h"
+
+#include "benchmark/benchmark.h"
+#include "benchmark_api_internal.h"
+#include "internal_macros.h"
+
+#ifndef BENCHMARK_OS_WINDOWS
+#ifndef BENCHMARK_OS_FUCHSIA
+#include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include "check.h"
+#include "colorprint.h"
+#include "commandlineflags.h"
+#include "complexity.h"
+#include "counter.h"
+#include "internal_macros.h"
+#include "log.h"
+#include "mutex.h"
+#include "perf_counters.h"
+#include "re.h"
+#include "statistics.h"
+#include "string_util.h"
+#include "thread_manager.h"
+#include "thread_timer.h"
+
+namespace benchmark {
+
+namespace internal {
+
+MemoryManager* memory_manager = nullptr;
+
+namespace {
+
+static constexpr IterationCount kMaxIterations = 1000000000;
+
+BenchmarkReporter::Run CreateRunReport(
+ const benchmark::internal::BenchmarkInstance& b,
+ const internal::ThreadManager::Result& results,
+ IterationCount memory_iterations,
+ const MemoryManager::Result& memory_result, double seconds,
+ int64_t repetition_index, int64_t repeats) {
+ // Create report about this benchmark run.
+ BenchmarkReporter::Run report;
+
+ report.run_name = b.name();
+ report.family_index = b.family_index();
+ report.per_family_instance_index = b.per_family_instance_index();
+ report.error_occurred = results.has_error_;
+ report.error_message = results.error_message_;
+ report.report_label = results.report_label_;
+ // This is the total iterations across all threads.
+ report.iterations = results.iterations;
+ report.time_unit = b.time_unit();
+ report.threads = b.threads();
+ report.repetition_index = repetition_index;
+ report.repetitions = repeats;
+
+ if (!report.error_occurred) {
+ if (b.use_manual_time()) {
+ report.real_accumulated_time = results.manual_time_used;
+ } else {
+ report.real_accumulated_time = results.real_time_used;
+ }
+ report.cpu_accumulated_time = results.cpu_time_used;
+ report.complexity_n = results.complexity_n;
+ report.complexity = b.complexity();
+ report.complexity_lambda = b.complexity_lambda();
+ report.statistics = &b.statistics();
+ report.counters = results.counters;
+
+ if (memory_iterations > 0) {
+ report.has_memory_result = true;
+ report.allocs_per_iter =
+ memory_iterations ? static_cast<double>(memory_result.num_allocs) /
+ memory_iterations
+ : 0;
+ report.max_bytes_used = memory_result.max_bytes_used;
+ }
+
+ internal::Finish(&report.counters, results.iterations, seconds,
+ b.threads());
+ }
+ return report;
+}
+
+// Execute one thread of benchmark b for the specified number of iterations.
+// Adds the stats collected for the thread into manager->results.
+void RunInThread(const BenchmarkInstance* b, IterationCount iters,
+ int thread_id, ThreadManager* manager,
+ PerfCountersMeasurement* perf_counters_measurement) {
+ internal::ThreadTimer timer(
+ b->measure_process_cpu_time()
+ ? internal::ThreadTimer::CreateProcessCpuTime()
+ : internal::ThreadTimer::Create());
+ State st =
+ b->Run(iters, thread_id, &timer, manager, perf_counters_measurement);
+ CHECK(st.error_occurred() || st.iterations() >= st.max_iterations)
+ << "Benchmark returned before State::KeepRunning() returned false!";
+ {
+ MutexLock l(manager->GetBenchmarkMutex());
+ internal::ThreadManager::Result& results = manager->results;
+ results.iterations += st.iterations();
+ results.cpu_time_used += timer.cpu_time_used();
+ results.real_time_used += timer.real_time_used();
+ results.manual_time_used += timer.manual_time_used();
+ results.complexity_n += st.complexity_length_n();
+ internal::Increment(&results.counters, st.counters);
+ }
+ manager->NotifyThreadComplete();
+}
+
+} // end namespace
+
+BenchmarkRunner::BenchmarkRunner(
+ const benchmark::internal::BenchmarkInstance& b_,
+ BenchmarkReporter::PerFamilyRunReports* reports_for_family_)
+ : b(b_),
+ reports_for_family(reports_for_family_),
+ min_time(!IsZero(b.min_time()) ? b.min_time() : FLAGS_benchmark_min_time),
+ repeats(b.repetitions() != 0 ? b.repetitions()
+ : FLAGS_benchmark_repetitions),
+ has_explicit_iteration_count(b.iterations() != 0),
+ pool(b.threads() - 1),
+ iters(has_explicit_iteration_count ? b.iterations() : 1),
+ perf_counters_measurement(
+ PerfCounters::Create(StrSplit(FLAGS_benchmark_perf_counters, ','))),
+ perf_counters_measurement_ptr(perf_counters_measurement.IsValid()
+ ? &perf_counters_measurement
+ : nullptr) {
+ run_results.display_report_aggregates_only =
+ (FLAGS_benchmark_report_aggregates_only ||
+ FLAGS_benchmark_display_aggregates_only);
+ run_results.file_report_aggregates_only =
+ FLAGS_benchmark_report_aggregates_only;
+ if (b.aggregation_report_mode() != internal::ARM_Unspecified) {
+ run_results.display_report_aggregates_only =
+ (b.aggregation_report_mode() &
+ internal::ARM_DisplayReportAggregatesOnly);
+ run_results.file_report_aggregates_only =
+ (b.aggregation_report_mode() & internal::ARM_FileReportAggregatesOnly);
+ CHECK(FLAGS_benchmark_perf_counters.empty() ||
+ perf_counters_measurement.IsValid())
+ << "Perf counters were requested but could not be set up.";
+ }
+}
+
+BenchmarkRunner::IterationResults BenchmarkRunner::DoNIterations() {
+ VLOG(2) << "Running " << b.name().str() << " for " << iters << "\n";
+
+ std::unique_ptr<internal::ThreadManager> manager;
+ manager.reset(new internal::ThreadManager(b.threads()));
+
+ // Run all but one thread in separate threads
+ for (std::size_t ti = 0; ti < pool.size(); ++ti) {
+ pool[ti] = std::thread(&RunInThread, &b, iters, static_cast<int>(ti + 1),
+ manager.get(), perf_counters_measurement_ptr);
+ }
+ // And run one thread here directly.
+ // (If we were asked to run just one thread, we don't create new threads.)
+ // Yes, we need to do this here *after* we start the separate threads.
+ RunInThread(&b, iters, 0, manager.get(), perf_counters_measurement_ptr);
+
+ // The main thread has finished. Now let's wait for the other threads.
+ manager->WaitForAllThreads();
+ for (std::thread& thread : pool) thread.join();
+
+ IterationResults i;
+ // Acquire the measurements/counters from the manager, UNDER THE LOCK!
+ {
+ MutexLock l(manager->GetBenchmarkMutex());
+ i.results = manager->results;
+ }
+
+ // And get rid of the manager.
+ manager.reset();
+
+ // Adjust real/manual time stats since they were reported per thread.
+ i.results.real_time_used /= b.threads();
+ i.results.manual_time_used /= b.threads();
+ // If we were measuring whole-process CPU usage, adjust the CPU time too.
+ if (b.measure_process_cpu_time()) i.results.cpu_time_used /= b.threads();
+
+ VLOG(2) << "Ran in " << i.results.cpu_time_used << "/"
+ << i.results.real_time_used << "\n";
+
+ // By using KeepRunningBatch a benchmark can iterate more times than
+ // requested, so take the iteration count from i.results.
+ i.iters = i.results.iterations / b.threads();
+
+ // Base decisions off of real time if requested by this benchmark.
+ i.seconds = i.results.cpu_time_used;
+ if (b.use_manual_time()) {
+ i.seconds = i.results.manual_time_used;
+ } else if (b.use_real_time()) {
+ i.seconds = i.results.real_time_used;
+ }
+
+ return i;
+}
+
+IterationCount BenchmarkRunner::PredictNumItersNeeded(
+ const IterationResults& i) const {
+ // See how much iterations should be increased by.
+ // Note: Avoid division by zero with max(seconds, 1ns).
+ double multiplier = min_time * 1.4 / std::max(i.seconds, 1e-9);
+ // If our last run was at least 10% of FLAGS_benchmark_min_time then we
+ // use the multiplier directly.
+ // Otherwise we use at most 10 times expansion.
+ // NOTE: When the last run was at least 10% of the min time the max
+ // expansion should be 14x.
+ bool is_significant = (i.seconds / min_time) > 0.1;
+ multiplier = is_significant ? multiplier : std::min(10.0, multiplier);
+ if (multiplier <= 1.0) multiplier = 2.0;
+
+ // So what seems to be the sufficiently-large iteration count? Round up.
+ const IterationCount max_next_iters = static_cast<IterationCount>(
+ std::lround(std::max(multiplier * static_cast<double>(i.iters),
+ static_cast<double>(i.iters) + 1.0)));
+ // But we do have *some* sanity limits though..
+ const IterationCount next_iters = std::min(max_next_iters, kMaxIterations);
+
+ VLOG(3) << "Next iters: " << next_iters << ", " << multiplier << "\n";
+ return next_iters; // round up before conversion to integer.
+}
+
+bool BenchmarkRunner::ShouldReportIterationResults(
+ const IterationResults& i) const {
+ // Determine if this run should be reported;
+ // Either it has run for a sufficient amount of time
+ // or because an error was reported.
+ return i.results.has_error_ ||
+ i.iters >= kMaxIterations || // Too many iterations already.
+ i.seconds >= min_time || // The elapsed time is large enough.
+ // CPU time is specified but the elapsed real time greatly exceeds
+ // the minimum time.
+ // Note that user provided timers are except from this sanity check.
+ ((i.results.real_time_used >= 5 * min_time) && !b.use_manual_time());
+}
+
+void BenchmarkRunner::DoOneRepetition() {
+ assert(HasRepeatsRemaining() && "Already done all repetitions?");
+
+ const bool is_the_first_repetition = num_repetitions_done == 0;
+ IterationResults i;
+
+ // We *may* be gradually increasing the length (iteration count)
+ // of the benchmark until we decide the results are significant.
+ // And once we do, we report those last results and exit.
+ // Please do note that the if there are repetitions, the iteration count
+ // is *only* calculated for the *first* repetition, and other repetitions
+ // simply use that precomputed iteration count.
+ for (;;) {
+ i = DoNIterations();
+
+ // Do we consider the results to be significant?
+ // If we are doing repetitions, and the first repetition was already done,
+ // it has calculated the correct iteration time, so we have run that very
+ // iteration count just now. No need to calculate anything. Just report.
+ // Else, the normal rules apply.
+ const bool results_are_significant = !is_the_first_repetition ||
+ has_explicit_iteration_count ||
+ ShouldReportIterationResults(i);
+
+ if (results_are_significant) break; // Good, let's report them!
+
+ // Nope, bad iteration. Let's re-estimate the hopefully-sufficient
+ // iteration count, and run the benchmark again...
+
+ iters = PredictNumItersNeeded(i);
+ assert(iters > i.iters &&
+ "if we did more iterations than we want to do the next time, "
+ "then we should have accepted the current iteration run.");
+ }
+
+ // Oh, one last thing, we need to also produce the 'memory measurements'..
+ MemoryManager::Result memory_result;
+ IterationCount memory_iterations = 0;
+ if (memory_manager != nullptr) {
+ // Only run a few iterations to reduce the impact of one-time
+ // allocations in benchmarks that are not properly managed.
+ memory_iterations = std::min<IterationCount>(16, iters);
+ memory_manager->Start();
+ std::unique_ptr<internal::ThreadManager> manager;
+ manager.reset(new internal::ThreadManager(1));
+ RunInThread(&b, memory_iterations, 0, manager.get(),
+ perf_counters_measurement_ptr);
+ manager->WaitForAllThreads();
+ manager.reset();
+
+ memory_manager->Stop(&memory_result);
+ }
+
+ // Ok, now actualy report.
+ BenchmarkReporter::Run report =
+ CreateRunReport(b, i.results, memory_iterations, memory_result, i.seconds,
+ num_repetitions_done, repeats);
+
+ if (reports_for_family) {
+ ++reports_for_family->num_runs_done;
+ if (!report.error_occurred) reports_for_family->Runs.push_back(report);
+ }
+
+ run_results.non_aggregates.push_back(report);
+
+ ++num_repetitions_done;
+}
+
+RunResults&& BenchmarkRunner::GetResults() {
+ assert(!HasRepeatsRemaining() && "Did not run all repetitions yet?");
+
+ // Calculate additional statistics over the repetitions of this instance.
+ run_results.aggregates_only = ComputeStats(run_results.non_aggregates);
+
+ return std::move(run_results);
+}
+
+} // end namespace internal
+
+} // end namespace benchmark
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BENCHMARK_RUNNER_H_
+#define BENCHMARK_RUNNER_H_
+
+#include <thread>
+#include <vector>
+
+#include "benchmark_api_internal.h"
+#include "internal_macros.h"
+#include "perf_counters.h"
+#include "thread_manager.h"
+
+DECLARE_double(benchmark_min_time);
+
+DECLARE_int32(benchmark_repetitions);
+
+DECLARE_bool(benchmark_report_aggregates_only);
+
+DECLARE_bool(benchmark_display_aggregates_only);
+
+DECLARE_string(benchmark_perf_counters);
+
+namespace benchmark {
+
+namespace internal {
+
+extern MemoryManager* memory_manager;
+
+struct RunResults {
+ std::vector<BenchmarkReporter::Run> non_aggregates;
+ std::vector<BenchmarkReporter::Run> aggregates_only;
+
+ bool display_report_aggregates_only = false;
+ bool file_report_aggregates_only = false;
+};
+
+class BenchmarkRunner {
+ public:
+ BenchmarkRunner(const benchmark::internal::BenchmarkInstance& b_,
+ BenchmarkReporter::PerFamilyRunReports* reports_for_family);
+
+ int GetNumRepeats() const { return repeats; }
+
+ bool HasRepeatsRemaining() const {
+ return GetNumRepeats() != num_repetitions_done;
+ }
+
+ void DoOneRepetition();
+
+ RunResults&& GetResults();
+
+ BenchmarkReporter::PerFamilyRunReports* GetReportsForFamily() const {
+ return reports_for_family;
+ };
+
+ private:
+ RunResults run_results;
+
+ const benchmark::internal::BenchmarkInstance& b;
+ BenchmarkReporter::PerFamilyRunReports* reports_for_family;
+
+ const double min_time;
+ const int repeats;
+ const bool has_explicit_iteration_count;
+
+ int num_repetitions_done = 0;
+
+ std::vector<std::thread> pool;
+
+ IterationCount iters; // preserved between repetitions!
+ // So only the first repetition has to find/calculate it,
+ // the other repetitions will just use that precomputed iteration count.
+
+ PerfCountersMeasurement perf_counters_measurement;
+ PerfCountersMeasurement* const perf_counters_measurement_ptr;
+
+ struct IterationResults {
+ internal::ThreadManager::Result results;
+ IterationCount iters;
+ double seconds;
+ };
+ IterationResults DoNIterations();
+
+ IterationCount PredictNumItersNeeded(const IterationResults& i) const;
+
+ bool ShouldReportIterationResults(const IterationResults& i) const;
+};
+
+} // namespace internal
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_RUNNER_H_
--- /dev/null
+#ifndef CHECK_H_
+#define CHECK_H_
+
+#include <cmath>
+#include <cstdlib>
+#include <ostream>
+
+#include "internal_macros.h"
+#include "log.h"
+
+namespace benchmark {
+namespace internal {
+
+typedef void(AbortHandlerT)();
+
+inline AbortHandlerT*& GetAbortHandler() {
+ static AbortHandlerT* handler = &std::abort;
+ return handler;
+}
+
+BENCHMARK_NORETURN inline void CallAbortHandler() {
+ GetAbortHandler()();
+ std::abort(); // fallback to enforce noreturn
+}
+
+// CheckHandler is the class constructed by failing CHECK macros. CheckHandler
+// will log information about the failures and abort when it is destructed.
+class CheckHandler {
+ public:
+ CheckHandler(const char* check, const char* file, const char* func, int line)
+ : log_(GetErrorLogInstance()) {
+ log_ << file << ":" << line << ": " << func << ": Check `" << check
+ << "' failed. ";
+ }
+
+ LogType& GetLog() { return log_; }
+
+ BENCHMARK_NORETURN ~CheckHandler() BENCHMARK_NOEXCEPT_OP(false) {
+ log_ << std::endl;
+ CallAbortHandler();
+ }
+
+ CheckHandler& operator=(const CheckHandler&) = delete;
+ CheckHandler(const CheckHandler&) = delete;
+ CheckHandler() = delete;
+
+ private:
+ LogType& log_;
+};
+
+} // end namespace internal
+} // end namespace benchmark
+
+// The CHECK macro returns a std::ostream object that can have extra information
+// written to it.
+#ifndef NDEBUG
+#define CHECK(b) \
+ (b ? ::benchmark::internal::GetNullLogInstance() \
+ : ::benchmark::internal::CheckHandler(#b, __FILE__, __func__, __LINE__) \
+ .GetLog())
+#else
+#define CHECK(b) ::benchmark::internal::GetNullLogInstance()
+#endif
+
+// clang-format off
+// preserve whitespacing between operators for alignment
+#define CHECK_EQ(a, b) CHECK((a) == (b))
+#define CHECK_NE(a, b) CHECK((a) != (b))
+#define CHECK_GE(a, b) CHECK((a) >= (b))
+#define CHECK_LE(a, b) CHECK((a) <= (b))
+#define CHECK_GT(a, b) CHECK((a) > (b))
+#define CHECK_LT(a, b) CHECK((a) < (b))
+
+#define CHECK_FLOAT_EQ(a, b, eps) CHECK(std::fabs((a) - (b)) < (eps))
+#define CHECK_FLOAT_NE(a, b, eps) CHECK(std::fabs((a) - (b)) >= (eps))
+#define CHECK_FLOAT_GE(a, b, eps) CHECK((a) - (b) > -(eps))
+#define CHECK_FLOAT_LE(a, b, eps) CHECK((b) - (a) > -(eps))
+#define CHECK_FLOAT_GT(a, b, eps) CHECK((a) - (b) > (eps))
+#define CHECK_FLOAT_LT(a, b, eps) CHECK((b) - (a) > (eps))
+//clang-format on
+
+#endif // CHECK_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "colorprint.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#include <string>
+
+#include "check.h"
+#include "internal_macros.h"
+
+#ifdef BENCHMARK_OS_WINDOWS
+#include <windows.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif // BENCHMARK_OS_WINDOWS
+
+namespace benchmark {
+namespace {
+#ifdef BENCHMARK_OS_WINDOWS
+typedef WORD PlatformColorCode;
+#else
+typedef const char* PlatformColorCode;
+#endif
+
+PlatformColorCode GetPlatformColorCode(LogColor color) {
+#ifdef BENCHMARK_OS_WINDOWS
+ switch (color) {
+ case COLOR_RED:
+ return FOREGROUND_RED;
+ case COLOR_GREEN:
+ return FOREGROUND_GREEN;
+ case COLOR_YELLOW:
+ return FOREGROUND_RED | FOREGROUND_GREEN;
+ case COLOR_BLUE:
+ return FOREGROUND_BLUE;
+ case COLOR_MAGENTA:
+ return FOREGROUND_BLUE | FOREGROUND_RED;
+ case COLOR_CYAN:
+ return FOREGROUND_BLUE | FOREGROUND_GREEN;
+ case COLOR_WHITE: // fall through to default
+ default:
+ return 0;
+ }
+#else
+ switch (color) {
+ case COLOR_RED:
+ return "1";
+ case COLOR_GREEN:
+ return "2";
+ case COLOR_YELLOW:
+ return "3";
+ case COLOR_BLUE:
+ return "4";
+ case COLOR_MAGENTA:
+ return "5";
+ case COLOR_CYAN:
+ return "6";
+ case COLOR_WHITE:
+ return "7";
+ default:
+ return nullptr;
+ };
+#endif
+}
+
+} // end namespace
+
+std::string FormatString(const char* msg, va_list args) {
+ // we might need a second shot at this, so pre-emptivly make a copy
+ va_list args_cp;
+ va_copy(args_cp, args);
+
+ std::size_t size = 256;
+ char local_buff[256];
+ auto ret = vsnprintf(local_buff, size, msg, args_cp);
+
+ va_end(args_cp);
+
+ // currently there is no error handling for failure, so this is hack.
+ CHECK(ret >= 0);
+
+ if (ret == 0) // handle empty expansion
+ return {};
+ else if (static_cast<size_t>(ret) < size)
+ return local_buff;
+ else {
+ // we did not provide a long enough buffer on our first attempt.
+ size = (size_t)ret + 1; // + 1 for the null byte
+ std::unique_ptr<char[]> buff(new char[size]);
+ ret = vsnprintf(buff.get(), size, msg, args);
+ CHECK(ret > 0 && ((size_t)ret) < size);
+ return buff.get();
+ }
+}
+
+std::string FormatString(const char* msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ auto tmp = FormatString(msg, args);
+ va_end(args);
+ return tmp;
+}
+
+void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ColorPrintf(out, color, fmt, args);
+ va_end(args);
+}
+
+void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
+ va_list args) {
+#ifdef BENCHMARK_OS_WINDOWS
+ ((void)out); // suppress unused warning
+
+ const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Gets the current text color.
+ CONSOLE_SCREEN_BUFFER_INFO buffer_info;
+ GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
+ const WORD old_color_attrs = buffer_info.wAttributes;
+
+ // We need to flush the stream buffers into the console before each
+ // SetConsoleTextAttribute call lest it affect the text that is already
+ // printed but has not yet reached the console.
+ fflush(stdout);
+ SetConsoleTextAttribute(stdout_handle,
+ GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
+ vprintf(fmt, args);
+
+ fflush(stdout);
+ // Restores the text color.
+ SetConsoleTextAttribute(stdout_handle, old_color_attrs);
+#else
+ const char* color_code = GetPlatformColorCode(color);
+ if (color_code) out << FormatString("\033[0;3%sm", color_code);
+ out << FormatString(fmt, args) << "\033[m";
+#endif
+}
+
+bool IsColorTerminal() {
+#if BENCHMARK_OS_WINDOWS
+ // On Windows the TERM variable is usually not set, but the
+ // console there does support colors.
+ return 0 != _isatty(_fileno(stdout));
+#else
+ // On non-Windows platforms, we rely on the TERM variable. This list of
+ // supported TERM values is copied from Google Test:
+ // <https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>.
+ const char* const SUPPORTED_TERM_VALUES[] = {
+ "xterm", "xterm-color", "xterm-256color",
+ "screen", "screen-256color", "tmux",
+ "tmux-256color", "rxvt-unicode", "rxvt-unicode-256color",
+ "linux", "cygwin",
+ };
+
+ const char* const term = getenv("TERM");
+
+ bool term_supports_color = false;
+ for (const char* candidate : SUPPORTED_TERM_VALUES) {
+ if (term && 0 == strcmp(term, candidate)) {
+ term_supports_color = true;
+ break;
+ }
+ }
+
+ return 0 != isatty(fileno(stdout)) && term_supports_color;
+#endif // BENCHMARK_OS_WINDOWS
+}
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_COLORPRINT_H_
+#define BENCHMARK_COLORPRINT_H_
+
+#include <cstdarg>
+#include <iostream>
+#include <string>
+
+namespace benchmark {
+enum LogColor {
+ COLOR_DEFAULT,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW,
+ COLOR_BLUE,
+ COLOR_MAGENTA,
+ COLOR_CYAN,
+ COLOR_WHITE
+};
+
+std::string FormatString(const char* msg, va_list args);
+std::string FormatString(const char* msg, ...);
+
+void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
+ va_list args);
+void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
+
+// Returns true if stdout appears to be a terminal that supports colored
+// output, false otherwise.
+bool IsColorTerminal();
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_COLORPRINT_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "commandlineflags.h"
+
+#include <algorithm>
+#include <cctype>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <utility>
+
+#include "../src/string_util.h"
+
+namespace benchmark {
+namespace {
+
+// Parses 'str' for a 32-bit signed integer. If successful, writes
+// the result to *value and returns true; otherwise leaves *value
+// unchanged and returns false.
+bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {
+ // Parses the environment variable as a decimal integer.
+ char* end = nullptr;
+ const long long_value = strtol(str, &end, 10); // NOLINT
+
+ // Has strtol() consumed all characters in the string?
+ if (*end != '\0') {
+ // No - an invalid character was encountered.
+ std::cerr << src_text << " is expected to be a 32-bit integer, "
+ << "but actually has value \"" << str << "\".\n";
+ return false;
+ }
+
+ // Is the parsed value in the range of an Int32?
+ const int32_t result = static_cast<int32_t>(long_value);
+ if (long_value == std::numeric_limits<long>::max() ||
+ long_value == std::numeric_limits<long>::min() ||
+ // The parsed value overflows as a long. (strtol() returns
+ // LONG_MAX or LONG_MIN when the input overflows.)
+ result != long_value
+ // The parsed value overflows as an Int32.
+ ) {
+ std::cerr << src_text << " is expected to be a 32-bit integer, "
+ << "but actually has value \"" << str << "\", "
+ << "which overflows.\n";
+ return false;
+ }
+
+ *value = result;
+ return true;
+}
+
+// Parses 'str' for a double. If successful, writes the result to *value and
+// returns true; otherwise leaves *value unchanged and returns false.
+bool ParseDouble(const std::string& src_text, const char* str, double* value) {
+ // Parses the environment variable as a decimal integer.
+ char* end = nullptr;
+ const double double_value = strtod(str, &end); // NOLINT
+
+ // Has strtol() consumed all characters in the string?
+ if (*end != '\0') {
+ // No - an invalid character was encountered.
+ std::cerr << src_text << " is expected to be a double, "
+ << "but actually has value \"" << str << "\".\n";
+ return false;
+ }
+
+ *value = double_value;
+ return true;
+}
+
+// Parses 'str' into KV pairs. If successful, writes the result to *value and
+// returns true; otherwise leaves *value unchanged and returns false.
+bool ParseKvPairs(const std::string& src_text, const char* str,
+ std::map<std::string, std::string>* value) {
+ std::map<std::string, std::string> kvs;
+ for (const auto& kvpair : StrSplit(str, ',')) {
+ const auto kv = StrSplit(kvpair, '=');
+ if (kv.size() != 2) {
+ std::cerr << src_text << " is expected to be a comma-separated list of "
+ << "<key>=<value> strings, but actually has value \"" << str
+ << "\".\n";
+ return false;
+ }
+ if (!kvs.emplace(kv[0], kv[1]).second) {
+ std::cerr << src_text << " is expected to contain unique keys but key \""
+ << kv[0] << "\" was repeated.\n";
+ return false;
+ }
+ }
+
+ *value = kvs;
+ return true;
+}
+
+// Returns the name of the environment variable corresponding to the
+// given flag. For example, FlagToEnvVar("foo") will return
+// "BENCHMARK_FOO" in the open-source version.
+static std::string FlagToEnvVar(const char* flag) {
+ const std::string flag_str(flag);
+
+ std::string env_var;
+ for (size_t i = 0; i != flag_str.length(); ++i)
+ env_var += static_cast<char>(::toupper(flag_str.c_str()[i]));
+
+ return env_var;
+}
+
+} // namespace
+
+bool BoolFromEnv(const char* flag, bool default_val) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value_str = getenv(env_var.c_str());
+ return value_str == nullptr ? default_val : IsTruthyFlagValue(value_str);
+}
+
+int32_t Int32FromEnv(const char* flag, int32_t default_val) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value_str = getenv(env_var.c_str());
+ int32_t value = default_val;
+ if (value_str == nullptr ||
+ !ParseInt32(std::string("Environment variable ") + env_var, value_str,
+ &value)) {
+ return default_val;
+ }
+ return value;
+}
+
+double DoubleFromEnv(const char* flag, double default_val) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value_str = getenv(env_var.c_str());
+ double value = default_val;
+ if (value_str == nullptr ||
+ !ParseDouble(std::string("Environment variable ") + env_var, value_str,
+ &value)) {
+ return default_val;
+ }
+ return value;
+}
+
+const char* StringFromEnv(const char* flag, const char* default_val) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value = getenv(env_var.c_str());
+ return value == nullptr ? default_val : value;
+}
+
+std::map<std::string, std::string> KvPairsFromEnv(
+ const char* flag, std::map<std::string, std::string> default_val) {
+ const std::string env_var = FlagToEnvVar(flag);
+ const char* const value_str = getenv(env_var.c_str());
+
+ if (value_str == nullptr) return default_val;
+
+ std::map<std::string, std::string> value;
+ if (!ParseKvPairs("Environment variable " + env_var, value_str, &value)) {
+ return default_val;
+ }
+ return value;
+}
+
+// Parses a string as a command line flag. The string should have
+// the format "--flag=value". When def_optional is true, the "=value"
+// part can be omitted.
+//
+// Returns the value of the flag, or nullptr if the parsing failed.
+const char* ParseFlagValue(const char* str, const char* flag,
+ bool def_optional) {
+ // str and flag must not be nullptr.
+ if (str == nullptr || flag == nullptr) return nullptr;
+
+ // The flag must start with "--".
+ const std::string flag_str = std::string("--") + std::string(flag);
+ const size_t flag_len = flag_str.length();
+ if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
+
+ // Skips the flag name.
+ const char* flag_end = str + flag_len;
+
+ // When def_optional is true, it's OK to not have a "=value" part.
+ if (def_optional && (flag_end[0] == '\0')) return flag_end;
+
+ // If def_optional is true and there are more characters after the
+ // flag name, or if def_optional is false, there must be a '=' after
+ // the flag name.
+ if (flag_end[0] != '=') return nullptr;
+
+ // Returns the string after "=".
+ return flag_end + 1;
+}
+
+bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, true);
+
+ // Aborts if the parsing failed.
+ if (value_str == nullptr) return false;
+
+ // Converts the string value to a bool.
+ *value = IsTruthyFlagValue(value_str);
+ return true;
+}
+
+bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == nullptr) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseInt32(std::string("The value of flag --") + flag, value_str,
+ value);
+}
+
+bool ParseDoubleFlag(const char* str, const char* flag, double* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == nullptr) return false;
+
+ // Sets *value to the value of the flag.
+ return ParseDouble(std::string("The value of flag --") + flag, value_str,
+ value);
+}
+
+bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
+ // Gets the value of the flag as a string.
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ // Aborts if the parsing failed.
+ if (value_str == nullptr) return false;
+
+ *value = value_str;
+ return true;
+}
+
+bool ParseKeyValueFlag(
+ const char* str, const char* flag,
+ std::map<std::string, std::string>* value) {
+ const char* const value_str = ParseFlagValue(str, flag, false);
+
+ if (value_str == nullptr) return false;
+
+ for (const auto& kvpair : StrSplit(value_str, ',')) {
+ const auto kv = StrSplit(kvpair, '=');
+ if (kv.size() != 2) return false;
+ value->emplace(kv[0], kv[1]);
+ }
+
+ return true;
+}
+
+bool IsFlag(const char* str, const char* flag) {
+ return (ParseFlagValue(str, flag, true) != nullptr);
+}
+
+bool IsTruthyFlagValue(const std::string& value) {
+ if (value.size() == 1) {
+ char v = value[0];
+ return isalnum(v) &&
+ !(v == '0' || v == 'f' || v == 'F' || v == 'n' || v == 'N');
+ } else if (!value.empty()) {
+ std::string value_lower(value);
+ std::transform(value_lower.begin(), value_lower.end(), value_lower.begin(),
+ [](char c) { return static_cast<char>(::tolower(c)); });
+ return !(value_lower == "false" || value_lower == "no" ||
+ value_lower == "off");
+ } else
+ return true;
+}
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_COMMANDLINEFLAGS_H_
+#define BENCHMARK_COMMANDLINEFLAGS_H_
+
+#include <cstdint>
+#include <map>
+#include <string>
+
+// Macro for referencing flags.
+#define FLAG(name) FLAGS_##name
+
+// Macros for declaring flags.
+#define DECLARE_bool(name) extern bool FLAG(name)
+#define DECLARE_int32(name) extern int32_t FLAG(name)
+#define DECLARE_double(name) extern double FLAG(name)
+#define DECLARE_string(name) extern std::string FLAG(name)
+#define DECLARE_kvpairs(name) \
+ extern std::map<std::string, std::string> FLAG(name)
+
+// Macros for defining flags.
+#define DEFINE_bool(name, default_val) \
+ bool FLAG(name) = benchmark::BoolFromEnv(#name, default_val)
+#define DEFINE_int32(name, default_val) \
+ int32_t FLAG(name) = benchmark::Int32FromEnv(#name, default_val)
+#define DEFINE_double(name, default_val) \
+ double FLAG(name) = benchmark::DoubleFromEnv(#name, default_val)
+#define DEFINE_string(name, default_val) \
+ std::string FLAG(name) = benchmark::StringFromEnv(#name, default_val)
+#define DEFINE_kvpairs(name, default_val) \
+ std::map<std::string, std::string> FLAG(name) = \
+ benchmark::KvPairsFromEnv(#name, default_val)
+
+namespace benchmark {
+
+// Parses a bool from the environment variable corresponding to the given flag.
+//
+// If the variable exists, returns IsTruthyFlagValue() value; if not,
+// returns the given default value.
+bool BoolFromEnv(const char* flag, bool default_val);
+
+// Parses an Int32 from the environment variable corresponding to the given
+// flag.
+//
+// If the variable exists, returns ParseInt32() value; if not, returns
+// the given default value.
+int32_t Int32FromEnv(const char* flag, int32_t default_val);
+
+// Parses an Double from the environment variable corresponding to the given
+// flag.
+//
+// If the variable exists, returns ParseDouble(); if not, returns
+// the given default value.
+double DoubleFromEnv(const char* flag, double default_val);
+
+// Parses a string from the environment variable corresponding to the given
+// flag.
+//
+// If variable exists, returns its value; if not, returns
+// the given default value.
+const char* StringFromEnv(const char* flag, const char* default_val);
+
+// Parses a set of kvpairs from the environment variable corresponding to the
+// given flag.
+//
+// If variable exists, returns its value; if not, returns
+// the given default value.
+std::map<std::string, std::string> KvPairsFromEnv(
+ const char* flag, std::map<std::string, std::string> default_val);
+
+// Parses a string for a bool flag, in the form of either
+// "--flag=value" or "--flag".
+//
+// In the former case, the value is taken as true if it passes IsTruthyValue().
+//
+// In the latter case, the value is taken as true.
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseBoolFlag(const char* str, const char* flag, bool* value);
+
+// Parses a string for an Int32 flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseInt32Flag(const char* str, const char* flag, int32_t* value);
+
+// Parses a string for a Double flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseDoubleFlag(const char* str, const char* flag, double* value);
+
+// Parses a string for a string flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true. On failure, returns false without changing *value.
+bool ParseStringFlag(const char* str, const char* flag, std::string* value);
+
+// Parses a string for a kvpairs flag in the form "--flag=key=value,key=value"
+//
+// On success, stores the value of the flag in *value and returns true. On
+// failure returns false, though *value may have been mutated.
+bool ParseKeyValueFlag(const char* str, const char* flag,
+ std::map<std::string, std::string>* value);
+
+// Returns true if the string matches the flag.
+bool IsFlag(const char* str, const char* flag);
+
+// Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or
+// some non-alphanumeric character. Also returns false if the value matches
+// one of 'no', 'false', 'off' (case-insensitive). As a special case, also
+// returns true if value is the empty string.
+bool IsTruthyFlagValue(const std::string& value);
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_COMMANDLINEFLAGS_H_
--- /dev/null
+// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Source project : https://github.com/ismaelJimenez/cpp.leastsq
+// Adapted to be used with google benchmark
+
+#include "benchmark/benchmark.h"
+
+#include <algorithm>
+#include <cmath>
+#include "check.h"
+#include "complexity.h"
+
+namespace benchmark {
+
+// Internal function to calculate the different scalability forms
+BigOFunc* FittingCurve(BigO complexity) {
+ static const double kLog2E = 1.44269504088896340736;
+ switch (complexity) {
+ case oN:
+ return [](IterationCount n) -> double { return static_cast<double>(n); };
+ case oNSquared:
+ return [](IterationCount n) -> double { return std::pow(n, 2); };
+ case oNCubed:
+ return [](IterationCount n) -> double { return std::pow(n, 3); };
+ case oLogN:
+ /* Note: can't use log2 because Android's GNU STL lacks it */
+ return
+ [](IterationCount n) { return kLog2E * log(static_cast<double>(n)); };
+ case oNLogN:
+ /* Note: can't use log2 because Android's GNU STL lacks it */
+ return [](IterationCount n) {
+ return kLog2E * n * log(static_cast<double>(n));
+ };
+ case o1:
+ default:
+ return [](IterationCount) { return 1.0; };
+ }
+}
+
+// Function to return an string for the calculated complexity
+std::string GetBigOString(BigO complexity) {
+ switch (complexity) {
+ case oN:
+ return "N";
+ case oNSquared:
+ return "N^2";
+ case oNCubed:
+ return "N^3";
+ case oLogN:
+ return "lgN";
+ case oNLogN:
+ return "NlgN";
+ case o1:
+ return "(1)";
+ default:
+ return "f(N)";
+ }
+}
+
+// Find the coefficient for the high-order term in the running time, by
+// minimizing the sum of squares of relative error, for the fitting curve
+// given by the lambda expression.
+// - n : Vector containing the size of the benchmark tests.
+// - time : Vector containing the times for the benchmark tests.
+// - fitting_curve : lambda expression (e.g. [](int64_t n) {return n; };).
+
+// For a deeper explanation on the algorithm logic, please refer to
+// https://en.wikipedia.org/wiki/Least_squares#Least_squares,_regression_analysis_and_statistics
+
+LeastSq MinimalLeastSq(const std::vector<int64_t>& n,
+ const std::vector<double>& time,
+ BigOFunc* fitting_curve) {
+ double sigma_gn_squared = 0.0;
+ double sigma_time = 0.0;
+ double sigma_time_gn = 0.0;
+
+ // Calculate least square fitting parameter
+ for (size_t i = 0; i < n.size(); ++i) {
+ double gn_i = fitting_curve(n[i]);
+ sigma_gn_squared += gn_i * gn_i;
+ sigma_time += time[i];
+ sigma_time_gn += time[i] * gn_i;
+ }
+
+ LeastSq result;
+ result.complexity = oLambda;
+
+ // Calculate complexity.
+ result.coef = sigma_time_gn / sigma_gn_squared;
+
+ // Calculate RMS
+ double rms = 0.0;
+ for (size_t i = 0; i < n.size(); ++i) {
+ double fit = result.coef * fitting_curve(n[i]);
+ rms += pow((time[i] - fit), 2);
+ }
+
+ // Normalized RMS by the mean of the observed values
+ double mean = sigma_time / n.size();
+ result.rms = sqrt(rms / n.size()) / mean;
+
+ return result;
+}
+
+// Find the coefficient for the high-order term in the running time, by
+// minimizing the sum of squares of relative error.
+// - n : Vector containing the size of the benchmark tests.
+// - time : Vector containing the times for the benchmark tests.
+// - complexity : If different than oAuto, the fitting curve will stick to
+// this one. If it is oAuto, it will be calculated the best
+// fitting curve.
+LeastSq MinimalLeastSq(const std::vector<int64_t>& n,
+ const std::vector<double>& time, const BigO complexity) {
+ CHECK_EQ(n.size(), time.size());
+ CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two
+ // benchmark runs are given
+ CHECK_NE(complexity, oNone);
+
+ LeastSq best_fit;
+
+ if (complexity == oAuto) {
+ std::vector<BigO> fit_curves = {oLogN, oN, oNLogN, oNSquared, oNCubed};
+
+ // Take o1 as default best fitting curve
+ best_fit = MinimalLeastSq(n, time, FittingCurve(o1));
+ best_fit.complexity = o1;
+
+ // Compute all possible fitting curves and stick to the best one
+ for (const auto& fit : fit_curves) {
+ LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit));
+ if (current_fit.rms < best_fit.rms) {
+ best_fit = current_fit;
+ best_fit.complexity = fit;
+ }
+ }
+ } else {
+ best_fit = MinimalLeastSq(n, time, FittingCurve(complexity));
+ best_fit.complexity = complexity;
+ }
+
+ return best_fit;
+}
+
+std::vector<BenchmarkReporter::Run> ComputeBigO(
+ const std::vector<BenchmarkReporter::Run>& reports) {
+ typedef BenchmarkReporter::Run Run;
+ std::vector<Run> results;
+
+ if (reports.size() < 2) return results;
+
+ // Accumulators.
+ std::vector<int64_t> n;
+ std::vector<double> real_time;
+ std::vector<double> cpu_time;
+
+ // Populate the accumulators.
+ for (const Run& run : reports) {
+ CHECK_GT(run.complexity_n, 0) << "Did you forget to call SetComplexityN?";
+ n.push_back(run.complexity_n);
+ real_time.push_back(run.real_accumulated_time / run.iterations);
+ cpu_time.push_back(run.cpu_accumulated_time / run.iterations);
+ }
+
+ LeastSq result_cpu;
+ LeastSq result_real;
+
+ if (reports[0].complexity == oLambda) {
+ result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda);
+ result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda);
+ } else {
+ result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity);
+ result_real = MinimalLeastSq(n, real_time, result_cpu.complexity);
+ }
+
+ // Drop the 'args' when reporting complexity.
+ auto run_name = reports[0].run_name;
+ run_name.args.clear();
+
+ // Get the data from the accumulator to BenchmarkReporter::Run's.
+ Run big_o;
+ big_o.run_name = run_name;
+ big_o.family_index = reports[0].family_index;
+ big_o.per_family_instance_index = reports[0].per_family_instance_index;
+ big_o.run_type = BenchmarkReporter::Run::RT_Aggregate;
+ big_o.repetitions = reports[0].repetitions;
+ big_o.repetition_index = Run::no_repetition_index;
+ big_o.threads = reports[0].threads;
+ big_o.aggregate_name = "BigO";
+ big_o.report_label = reports[0].report_label;
+ big_o.iterations = 0;
+ big_o.real_accumulated_time = result_real.coef;
+ big_o.cpu_accumulated_time = result_cpu.coef;
+ big_o.report_big_o = true;
+ big_o.complexity = result_cpu.complexity;
+
+ // All the time results are reported after being multiplied by the
+ // time unit multiplier. But since RMS is a relative quantity it
+ // should not be multiplied at all. So, here, we _divide_ it by the
+ // multiplier so that when it is multiplied later the result is the
+ // correct one.
+ double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);
+
+ // Only add label to mean/stddev if it is same for all runs
+ Run rms;
+ rms.run_name = run_name;
+ rms.family_index = reports[0].family_index;
+ rms.per_family_instance_index = reports[0].per_family_instance_index;
+ rms.run_type = BenchmarkReporter::Run::RT_Aggregate;
+ rms.aggregate_name = "RMS";
+ rms.report_label = big_o.report_label;
+ rms.iterations = 0;
+ rms.repetition_index = Run::no_repetition_index;
+ rms.repetitions = reports[0].repetitions;
+ rms.threads = reports[0].threads;
+ rms.real_accumulated_time = result_real.rms / multiplier;
+ rms.cpu_accumulated_time = result_cpu.rms / multiplier;
+ rms.report_rms = true;
+ rms.complexity = result_cpu.complexity;
+ // don't forget to keep the time unit, or we won't be able to
+ // recover the correct value.
+ rms.time_unit = reports[0].time_unit;
+
+ results.push_back(big_o);
+ results.push_back(rms);
+ return results;
+}
+
+} // end namespace benchmark
--- /dev/null
+// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Source project : https://github.com/ismaelJimenez/cpp.leastsq
+// Adapted to be used with google benchmark
+
+#ifndef COMPLEXITY_H_
+#define COMPLEXITY_H_
+
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+namespace benchmark {
+
+// Return a vector containing the bigO and RMS information for the specified
+// list of reports. If 'reports.size() < 2' an empty vector is returned.
+std::vector<BenchmarkReporter::Run> ComputeBigO(
+ const std::vector<BenchmarkReporter::Run>& reports);
+
+// This data structure will contain the result returned by MinimalLeastSq
+// - coef : Estimated coeficient for the high-order term as
+// interpolated from data.
+// - rms : Normalized Root Mean Squared Error.
+// - complexity : Scalability form (e.g. oN, oNLogN). In case a scalability
+// form has been provided to MinimalLeastSq this will return
+// the same value. In case BigO::oAuto has been selected, this
+// parameter will return the best fitting curve detected.
+
+struct LeastSq {
+ LeastSq() : coef(0.0), rms(0.0), complexity(oNone) {}
+
+ double coef;
+ double rms;
+ BigO complexity;
+};
+
+// Function to return an string for the calculated complexity
+std::string GetBigOString(BigO complexity);
+
+} // end namespace benchmark
+
+#endif // COMPLEXITY_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "check.h"
+#include "colorprint.h"
+#include "commandlineflags.h"
+#include "complexity.h"
+#include "counter.h"
+#include "internal_macros.h"
+#include "string_util.h"
+#include "timers.h"
+
+namespace benchmark {
+
+bool ConsoleReporter::ReportContext(const Context& context) {
+ name_field_width_ = context.name_field_width;
+ printed_header_ = false;
+ prev_counters_.clear();
+
+ PrintBasicContext(&GetErrorStream(), context);
+
+#ifdef BENCHMARK_OS_WINDOWS
+ if ((output_options_ & OO_Color) && &std::cout != &GetOutputStream()) {
+ GetErrorStream()
+ << "Color printing is only supported for stdout on windows."
+ " Disabling color printing\n";
+ output_options_ = static_cast< OutputOptions >(output_options_ & ~OO_Color);
+ }
+#endif
+
+ return true;
+}
+
+void ConsoleReporter::PrintHeader(const Run& run) {
+ std::string str = FormatString("%-*s %13s %15s %12s", static_cast<int>(name_field_width_),
+ "Benchmark", "Time", "CPU", "Iterations");
+ if(!run.counters.empty()) {
+ if(output_options_ & OO_Tabular) {
+ for(auto const& c : run.counters) {
+ str += FormatString(" %10s", c.first.c_str());
+ }
+ } else {
+ str += " UserCounters...";
+ }
+ }
+ std::string line = std::string(str.length(), '-');
+ GetOutputStream() << line << "\n" << str << "\n" << line << "\n";
+}
+
+void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) {
+ for (const auto& run : reports) {
+ // print the header:
+ // --- if none was printed yet
+ bool print_header = !printed_header_;
+ // --- or if the format is tabular and this run
+ // has different fields from the prev header
+ print_header |= (output_options_ & OO_Tabular) &&
+ (!internal::SameNames(run.counters, prev_counters_));
+ if (print_header) {
+ printed_header_ = true;
+ prev_counters_ = run.counters;
+ PrintHeader(run);
+ }
+ // As an alternative to printing the headers like this, we could sort
+ // the benchmarks by header and then print. But this would require
+ // waiting for the full results before printing, or printing twice.
+ PrintRunData(run);
+ }
+}
+
+static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt,
+ ...) {
+ va_list args;
+ va_start(args, fmt);
+ out << FormatString(fmt, args);
+ va_end(args);
+}
+
+
+static std::string FormatTime(double time) {
+ // Align decimal places...
+ if (time < 1.0) {
+ return FormatString("%10.3f", time);
+ }
+ if (time < 10.0) {
+ return FormatString("%10.2f", time);
+ }
+ if (time < 100.0) {
+ return FormatString("%10.1f", time);
+ }
+ return FormatString("%10.0f", time);
+}
+
+void ConsoleReporter::PrintRunData(const Run& result) {
+ typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...);
+ auto& Out = GetOutputStream();
+ PrinterFn* printer = (output_options_ & OO_Color) ?
+ (PrinterFn*)ColorPrintf : IgnoreColorPrint;
+ auto name_color =
+ (result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN;
+ printer(Out, name_color, "%-*s ", name_field_width_,
+ result.benchmark_name().c_str());
+
+ if (result.error_occurred) {
+ printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
+ result.error_message.c_str());
+ printer(Out, COLOR_DEFAULT, "\n");
+ return;
+ }
+
+ const double real_time = result.GetAdjustedRealTime();
+ const double cpu_time = result.GetAdjustedCPUTime();
+ const std::string real_time_str = FormatTime(real_time);
+ const std::string cpu_time_str = FormatTime(cpu_time);
+
+
+ if (result.report_big_o) {
+ std::string big_o = GetBigOString(result.complexity);
+ printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ", real_time, big_o.c_str(),
+ cpu_time, big_o.c_str());
+ } else if (result.report_rms) {
+ printer(Out, COLOR_YELLOW, "%10.0f %-4s %10.0f %-4s ", real_time * 100, "%",
+ cpu_time * 100, "%");
+ } else {
+ const char* timeLabel = GetTimeUnitString(result.time_unit);
+ printer(Out, COLOR_YELLOW, "%s %-4s %s %-4s ", real_time_str.c_str(), timeLabel,
+ cpu_time_str.c_str(), timeLabel);
+ }
+
+ if (!result.report_big_o && !result.report_rms) {
+ printer(Out, COLOR_CYAN, "%10lld", result.iterations);
+ }
+
+ for (auto& c : result.counters) {
+ const std::size_t cNameLen = std::max(std::string::size_type(10),
+ c.first.length());
+ auto const& s = HumanReadableNumber(c.second.value, c.second.oneK);
+ const char* unit = "";
+ if (c.second.flags & Counter::kIsRate)
+ unit = (c.second.flags & Counter::kInvert) ? "s" : "/s";
+ if (output_options_ & OO_Tabular) {
+ printer(Out, COLOR_DEFAULT, " %*s%s", cNameLen - strlen(unit), s.c_str(),
+ unit);
+ } else {
+ printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), unit);
+ }
+ }
+
+ if (!result.report_label.empty()) {
+ printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
+ }
+
+ printer(Out, COLOR_DEFAULT, "\n");
+}
+
+} // end namespace benchmark
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "counter.h"
+
+namespace benchmark {
+namespace internal {
+
+double Finish(Counter const& c, IterationCount iterations, double cpu_time,
+ double num_threads) {
+ double v = c.value;
+ if (c.flags & Counter::kIsRate) {
+ v /= cpu_time;
+ }
+ if (c.flags & Counter::kAvgThreads) {
+ v /= num_threads;
+ }
+ if (c.flags & Counter::kIsIterationInvariant) {
+ v *= iterations;
+ }
+ if (c.flags & Counter::kAvgIterations) {
+ v /= iterations;
+ }
+
+ if (c.flags & Counter::kInvert) { // Invert is *always* last.
+ v = 1.0 / v;
+ }
+ return v;
+}
+
+void Finish(UserCounters* l, IterationCount iterations, double cpu_time,
+ double num_threads) {
+ for (auto& c : *l) {
+ c.second.value = Finish(c.second, iterations, cpu_time, num_threads);
+ }
+}
+
+void Increment(UserCounters* l, UserCounters const& r) {
+ // add counters present in both or just in *l
+ for (auto& c : *l) {
+ auto it = r.find(c.first);
+ if (it != r.end()) {
+ c.second.value = c.second + it->second;
+ }
+ }
+ // add counters present in r, but not in *l
+ for (auto const& tc : r) {
+ auto it = l->find(tc.first);
+ if (it == l->end()) {
+ (*l)[tc.first] = tc.second;
+ }
+ }
+}
+
+bool SameNames(UserCounters const& l, UserCounters const& r) {
+ if (&l == &r) return true;
+ if (l.size() != r.size()) {
+ return false;
+ }
+ for (auto const& c : l) {
+ if (r.find(c.first) == r.end()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // end namespace internal
+} // end namespace benchmark
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BENCHMARK_COUNTER_H_
+#define BENCHMARK_COUNTER_H_
+
+#include "benchmark/benchmark.h"
+
+namespace benchmark {
+
+// these counter-related functions are hidden to reduce API surface.
+namespace internal {
+void Finish(UserCounters* l, IterationCount iterations, double time,
+ double num_threads);
+void Increment(UserCounters* l, UserCounters const& r);
+bool SameNames(UserCounters const& l, UserCounters const& r);
+} // end namespace internal
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_COUNTER_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+#include "complexity.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "check.h"
+#include "string_util.h"
+#include "timers.h"
+
+// File format reference: http://edoceo.com/utilitas/csv-file-format.
+
+namespace benchmark {
+
+namespace {
+std::vector<std::string> elements = {
+ "name", "iterations", "real_time", "cpu_time",
+ "time_unit", "bytes_per_second", "items_per_second", "label",
+ "error_occurred", "error_message"};
+} // namespace
+
+std::string CsvEscape(const std::string & s) {
+ std::string tmp;
+ tmp.reserve(s.size() + 2);
+ for (char c : s) {
+ switch (c) {
+ case '"' : tmp += "\"\""; break;
+ default : tmp += c; break;
+ }
+ }
+ return '"' + tmp + '"';
+}
+
+bool CSVReporter::ReportContext(const Context& context) {
+ PrintBasicContext(&GetErrorStream(), context);
+ return true;
+}
+
+void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
+ std::ostream& Out = GetOutputStream();
+
+ if (!printed_header_) {
+ // save the names of all the user counters
+ for (const auto& run : reports) {
+ for (const auto& cnt : run.counters) {
+ if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
+ continue;
+ user_counter_names_.insert(cnt.first);
+ }
+ }
+
+ // print the header
+ for (auto B = elements.begin(); B != elements.end();) {
+ Out << *B++;
+ if (B != elements.end()) Out << ",";
+ }
+ for (auto B = user_counter_names_.begin();
+ B != user_counter_names_.end();) {
+ Out << ",\"" << *B++ << "\"";
+ }
+ Out << "\n";
+
+ printed_header_ = true;
+ } else {
+ // check that all the current counters are saved in the name set
+ for (const auto& run : reports) {
+ for (const auto& cnt : run.counters) {
+ if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
+ continue;
+ CHECK(user_counter_names_.find(cnt.first) != user_counter_names_.end())
+ << "All counters must be present in each run. "
+ << "Counter named \"" << cnt.first
+ << "\" was not in a run after being added to the header";
+ }
+ }
+ }
+
+ // print results for each run
+ for (const auto& run : reports) {
+ PrintRunData(run);
+ }
+}
+
+void CSVReporter::PrintRunData(const Run& run) {
+ std::ostream& Out = GetOutputStream();
+ Out << CsvEscape(run.benchmark_name()) << ",";
+ if (run.error_occurred) {
+ Out << std::string(elements.size() - 3, ',');
+ Out << "true,";
+ Out << CsvEscape(run.error_message) << "\n";
+ return;
+ }
+
+ // Do not print iteration on bigO and RMS report
+ if (!run.report_big_o && !run.report_rms) {
+ Out << run.iterations;
+ }
+ Out << ",";
+
+ Out << run.GetAdjustedRealTime() << ",";
+ Out << run.GetAdjustedCPUTime() << ",";
+
+ // Do not print timeLabel on bigO and RMS report
+ if (run.report_big_o) {
+ Out << GetBigOString(run.complexity);
+ } else if (!run.report_rms) {
+ Out << GetTimeUnitString(run.time_unit);
+ }
+ Out << ",";
+
+ if (run.counters.find("bytes_per_second") != run.counters.end()) {
+ Out << run.counters.at("bytes_per_second");
+ }
+ Out << ",";
+ if (run.counters.find("items_per_second") != run.counters.end()) {
+ Out << run.counters.at("items_per_second");
+ }
+ Out << ",";
+ if (!run.report_label.empty()) {
+ Out << CsvEscape(run.report_label);
+ }
+ Out << ",,"; // for error_occurred and error_message
+
+ // Print user counters
+ for (const auto& ucn : user_counter_names_) {
+ auto it = run.counters.find(ucn);
+ if (it == run.counters.end()) {
+ Out << ",";
+ } else {
+ Out << "," << it->second;
+ }
+ }
+ Out << '\n';
+}
+
+} // end namespace benchmark
--- /dev/null
+// ----------------------------------------------------------------------
+// CycleClock
+// A CycleClock tells you the current time in Cycles. The "time"
+// is actually time since power-on. This is like time() but doesn't
+// involve a system call and is much more precise.
+//
+// NOTE: Not all cpu/platform/kernel combinations guarantee that this
+// clock increments at a constant rate or is synchronized across all logical
+// cpus in a system.
+//
+// If you need the above guarantees, please consider using a different
+// API. There are efforts to provide an interface which provides a millisecond
+// granularity and implemented as a memory read. A memory read is generally
+// cheaper than the CycleClock for many architectures.
+//
+// Also, in some out of order CPU implementations, the CycleClock is not
+// serializing. So if you're trying to count at cycles granularity, your
+// data might be inaccurate due to out of order instruction execution.
+// ----------------------------------------------------------------------
+
+#ifndef BENCHMARK_CYCLECLOCK_H_
+#define BENCHMARK_CYCLECLOCK_H_
+
+#include <cstdint>
+
+#include "benchmark/benchmark.h"
+#include "internal_macros.h"
+
+#if defined(BENCHMARK_OS_MACOSX)
+#include <mach/mach_time.h>
+#endif
+// For MSVC, we want to use '_asm rdtsc' when possible (since it works
+// with even ancient MSVC compilers), and when not possible the
+// __rdtsc intrinsic, declared in <intrin.h>. Unfortunately, in some
+// environments, <windows.h> and <intrin.h> have conflicting
+// declarations of some other intrinsics, breaking compilation.
+// Therefore, we simply declare __rdtsc ourselves. See also
+// http://connect.microsoft.com/VisualStudio/feedback/details/262047
+#if defined(COMPILER_MSVC) && !defined(_M_IX86) && !defined(_M_ARM64)
+extern "C" uint64_t __rdtsc();
+#pragma intrinsic(__rdtsc)
+#endif
+
+#if !defined(BENCHMARK_OS_WINDOWS) || defined(BENCHMARK_OS_MINGW)
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#ifdef BENCHMARK_OS_EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+namespace benchmark {
+// NOTE: only i386 and x86_64 have been well tested.
+// PPC, sparc, alpha, and ia64 are based on
+// http://peter.kuscsik.com/wordpress/?p=14
+// with modifications by m3b. See also
+// https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
+namespace cycleclock {
+// This should return the number of cycles since power-on. Thread-safe.
+inline BENCHMARK_ALWAYS_INLINE int64_t Now() {
+#if defined(BENCHMARK_OS_MACOSX)
+ // this goes at the top because we need ALL Macs, regardless of
+ // architecture, to return the number of "mach time units" that
+ // have passed since startup. See sysinfo.cc where
+ // InitializeSystemInfo() sets the supposed cpu clock frequency of
+ // macs to the number of mach time units per second, not actual
+ // CPU clock frequency (which can change in the face of CPU
+ // frequency scaling). Also note that when the Mac sleeps, this
+ // counter pauses; it does not continue counting, nor does it
+ // reset to zero.
+ return mach_absolute_time();
+#elif defined(BENCHMARK_OS_EMSCRIPTEN)
+ // this goes above x86-specific code because old versions of Emscripten
+ // define __x86_64__, although they have nothing to do with it.
+ return static_cast<int64_t>(emscripten_get_now() * 1e+6);
+#elif defined(__i386__)
+ int64_t ret;
+ __asm__ volatile("rdtsc" : "=A"(ret));
+ return ret;
+#elif defined(__x86_64__) || defined(__amd64__)
+ uint64_t low, high;
+ __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
+ return (high << 32) | low;
+#elif defined(__powerpc__) || defined(__ppc__)
+ // This returns a time-base, which is not always precisely a cycle-count.
+#if defined(__powerpc64__) || defined(__ppc64__)
+ int64_t tb;
+ asm volatile("mfspr %0, 268" : "=r"(tb));
+ return tb;
+#else
+ uint32_t tbl, tbu0, tbu1;
+ asm volatile(
+ "mftbu %0\n"
+ "mftb %1\n"
+ "mftbu %2"
+ : "=r"(tbu0), "=r"(tbl), "=r"(tbu1));
+ tbl &= -static_cast<int32_t>(tbu0 == tbu1);
+ // high 32 bits in tbu1; low 32 bits in tbl (tbu0 is no longer needed)
+ return (static_cast<uint64_t>(tbu1) << 32) | tbl;
+#endif
+#elif defined(__sparc__)
+ int64_t tick;
+ asm(".byte 0x83, 0x41, 0x00, 0x00");
+ asm("mov %%g1, %0" : "=r"(tick));
+ return tick;
+#elif defined(__ia64__)
+ int64_t itc;
+ asm("mov %0 = ar.itc" : "=r"(itc));
+ return itc;
+#elif defined(COMPILER_MSVC) && defined(_M_IX86)
+ // Older MSVC compilers (like 7.x) don't seem to support the
+ // __rdtsc intrinsic properly, so I prefer to use _asm instead
+ // when I know it will work. Otherwise, I'll use __rdtsc and hope
+ // the code is being compiled with a non-ancient compiler.
+ _asm rdtsc
+#elif defined(COMPILER_MSVC) && defined(_M_ARM64)
+ // See https://docs.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=vs-2019
+ // and https://reviews.llvm.org/D53115
+ int64_t virtual_timer_value;
+ virtual_timer_value = _ReadStatusReg(ARM64_CNTVCT);
+ return virtual_timer_value;
+#elif defined(COMPILER_MSVC)
+ return __rdtsc();
+#elif defined(BENCHMARK_OS_NACL)
+ // Native Client validator on x86/x86-64 allows RDTSC instructions,
+ // and this case is handled above. Native Client validator on ARM
+ // rejects MRC instructions (used in the ARM-specific sequence below),
+ // so we handle it here. Portable Native Client compiles to
+ // architecture-agnostic bytecode, which doesn't provide any
+ // cycle counter access mnemonics.
+
+ // Native Client does not provide any API to access cycle counter.
+ // Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday
+ // because is provides nanosecond resolution (which is noticable at
+ // least for PNaCl modules running on x86 Mac & Linux).
+ // Initialize to always return 0 if clock_gettime fails.
+ struct timespec ts = {0, 0};
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
+#elif defined(__aarch64__)
+ // System timer of ARMv8 runs at a different frequency than the CPU's.
+ // The frequency is fixed, typically in the range 1-50MHz. It can be
+ // read at CNTFRQ special register. We assume the OS has set up
+ // the virtual timer properly.
+ int64_t virtual_timer_value;
+ asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
+ return virtual_timer_value;
+#elif defined(__ARM_ARCH)
+ // V6 is the earliest arch that has a standard cyclecount
+ // Native Client validator doesn't allow MRC instructions.
+#if (__ARM_ARCH >= 6)
+ uint32_t pmccntr;
+ uint32_t pmuseren;
+ uint32_t pmcntenset;
+ // Read the user mode perf monitor counter access permissions.
+ asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
+ if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
+ asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
+ if (pmcntenset & 0x80000000ul) { // Is it counting?
+ asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
+ // The counter is set up to count every 64th cycle
+ return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
+ }
+ }
+#endif
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+#elif defined(__mips__) || defined(__m68k__)
+ // mips apparently only allows rdtsc for superusers, so we fall
+ // back to gettimeofday. It's possible clock_gettime would be better.
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+#elif defined(__loongarch__)
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+#elif defined(__s390__) // Covers both s390 and s390x.
+ // Return the CPU clock.
+ uint64_t tsc;
+#if defined(BENCHMARK_OS_ZOS) && defined(COMPILER_IBMXL)
+ // z/OS XL compiler HLASM syntax.
+ asm(" stck %0" : "=m"(tsc) : : "cc");
+#else
+ asm("stck %0" : "=Q"(tsc) : : "cc");
+#endif
+ return tsc;
+#elif defined(__riscv) // RISC-V
+ // Use RDCYCLE (and RDCYCLEH on riscv32)
+#if __riscv_xlen == 32
+ uint32_t cycles_lo, cycles_hi0, cycles_hi1;
+ // This asm also includes the PowerPC overflow handling strategy, as above.
+ // Implemented in assembly because Clang insisted on branching.
+ asm volatile(
+ "rdcycleh %0\n"
+ "rdcycle %1\n"
+ "rdcycleh %2\n"
+ "sub %0, %0, %2\n"
+ "seqz %0, %0\n"
+ "sub %0, zero, %0\n"
+ "and %1, %1, %0\n"
+ : "=r"(cycles_hi0), "=r"(cycles_lo), "=r"(cycles_hi1));
+ return (static_cast<uint64_t>(cycles_hi1) << 32) | cycles_lo;
+#else
+ uint64_t cycles;
+ asm volatile("rdcycle %0" : "=r"(cycles));
+ return cycles;
+#endif
+#elif defined(__e2k__) || defined(__elbrus__)
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
+#else
+// The soft failover to a generic implementation is automatic only for ARM.
+// For other platforms the developer is expected to make an attempt to create
+// a fast implementation and use generic version if nothing better is available.
+#error You need to define CycleTimer for your OS and CPU
+#endif
+}
+} // end namespace cycleclock
+} // end namespace benchmark
+
+#endif // BENCHMARK_CYCLECLOCK_H_
--- /dev/null
+#ifndef BENCHMARK_INTERNAL_MACROS_H_
+#define BENCHMARK_INTERNAL_MACROS_H_
+
+#include "benchmark/benchmark.h"
+
+/* Needed to detect STL */
+#include <cstdlib>
+
+// clang-format off
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if defined(__clang__)
+ #if defined(__ibmxl__)
+ #if !defined(COMPILER_IBMXL)
+ #define COMPILER_IBMXL
+ #endif
+ #elif !defined(COMPILER_CLANG)
+ #define COMPILER_CLANG
+ #endif
+#elif defined(_MSC_VER)
+ #if !defined(COMPILER_MSVC)
+ #define COMPILER_MSVC
+ #endif
+#elif defined(__GNUC__)
+ #if !defined(COMPILER_GCC)
+ #define COMPILER_GCC
+ #endif
+#endif
+
+#if __has_feature(cxx_attributes)
+ #define BENCHMARK_NORETURN [[noreturn]]
+#elif defined(__GNUC__)
+ #define BENCHMARK_NORETURN __attribute__((noreturn))
+#elif defined(COMPILER_MSVC)
+ #define BENCHMARK_NORETURN __declspec(noreturn)
+#else
+ #define BENCHMARK_NORETURN
+#endif
+
+#if defined(__CYGWIN__)
+ #define BENCHMARK_OS_CYGWIN 1
+#elif defined(_WIN32)
+ #define BENCHMARK_OS_WINDOWS 1
+ #if defined(__MINGW32__)
+ #define BENCHMARK_OS_MINGW 1
+ #endif
+#elif defined(__APPLE__)
+ #define BENCHMARK_OS_APPLE 1
+ #include "TargetConditionals.h"
+ #if defined(TARGET_OS_MAC)
+ #define BENCHMARK_OS_MACOSX 1
+ #if defined(TARGET_OS_IPHONE)
+ #define BENCHMARK_OS_IOS 1
+ #endif
+ #endif
+#elif defined(__FreeBSD__)
+ #define BENCHMARK_OS_FREEBSD 1
+#elif defined(__NetBSD__)
+ #define BENCHMARK_OS_NETBSD 1
+#elif defined(__OpenBSD__)
+ #define BENCHMARK_OS_OPENBSD 1
+#elif defined(__DragonFly__)
+ #define BENCHMARK_OS_DRAGONFLY 1
+#elif defined(__linux__)
+ #define BENCHMARK_OS_LINUX 1
+#elif defined(__native_client__)
+ #define BENCHMARK_OS_NACL 1
+#elif defined(__EMSCRIPTEN__)
+ #define BENCHMARK_OS_EMSCRIPTEN 1
+#elif defined(__rtems__)
+ #define BENCHMARK_OS_RTEMS 1
+#elif defined(__Fuchsia__)
+#define BENCHMARK_OS_FUCHSIA 1
+#elif defined (__SVR4) && defined (__sun)
+#define BENCHMARK_OS_SOLARIS 1
+#elif defined(__QNX__)
+#define BENCHMARK_OS_QNX 1
+#elif defined(__MVS__)
+#define BENCHMARK_OS_ZOS 1
+#endif
+
+#if defined(__ANDROID__) && defined(__GLIBCXX__)
+#define BENCHMARK_STL_ANDROID_GNUSTL 1
+#endif
+
+#if !__has_feature(cxx_exceptions) && !defined(__cpp_exceptions) \
+ && !defined(__EXCEPTIONS)
+ #define BENCHMARK_HAS_NO_EXCEPTIONS
+#endif
+
+#if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
+ #define BENCHMARK_MAYBE_UNUSED __attribute__((unused))
+#else
+ #define BENCHMARK_MAYBE_UNUSED
+#endif
+
+// clang-format on
+
+#endif // BENCHMARK_INTERNAL_MACROS_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+#include "complexity.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <iomanip> // for setprecision
+#include <iostream>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "string_util.h"
+#include "timers.h"
+
+namespace benchmark {
+namespace internal {
+extern std::map<std::string, std::string>* global_context;
+}
+
+namespace {
+
+std::string StrEscape(const std::string & s) {
+ std::string tmp;
+ tmp.reserve(s.size());
+ for (char c : s) {
+ switch (c) {
+ case '\b': tmp += "\\b"; break;
+ case '\f': tmp += "\\f"; break;
+ case '\n': tmp += "\\n"; break;
+ case '\r': tmp += "\\r"; break;
+ case '\t': tmp += "\\t"; break;
+ case '\\': tmp += "\\\\"; break;
+ case '"' : tmp += "\\\""; break;
+ default : tmp += c; break;
+ }
+ }
+ return tmp;
+}
+
+std::string FormatKV(std::string const& key, std::string const& value) {
+ return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(), StrEscape(value).c_str());
+}
+
+std::string FormatKV(std::string const& key, const char* value) {
+ return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(), StrEscape(value).c_str());
+}
+
+std::string FormatKV(std::string const& key, bool value) {
+ return StrFormat("\"%s\": %s", StrEscape(key).c_str(), value ? "true" : "false");
+}
+
+std::string FormatKV(std::string const& key, int64_t value) {
+ std::stringstream ss;
+ ss << '"' << StrEscape(key) << "\": " << value;
+ return ss.str();
+}
+
+std::string FormatKV(std::string const& key, IterationCount value) {
+ std::stringstream ss;
+ ss << '"' << StrEscape(key) << "\": " << value;
+ return ss.str();
+}
+
+std::string FormatKV(std::string const& key, double value) {
+ std::stringstream ss;
+ ss << '"' << StrEscape(key) << "\": ";
+
+ if (std::isnan(value))
+ ss << (value < 0 ? "-" : "") << "NaN";
+ else if (std::isinf(value))
+ ss << (value < 0 ? "-" : "") << "Infinity";
+ else {
+ const auto max_digits10 =
+ std::numeric_limits<decltype(value)>::max_digits10;
+ const auto max_fractional_digits10 = max_digits10 - 1;
+ ss << std::scientific << std::setprecision(max_fractional_digits10)
+ << value;
+ }
+ return ss.str();
+}
+
+int64_t RoundDouble(double v) { return std::lround(v); }
+
+} // end namespace
+
+bool JSONReporter::ReportContext(const Context& context) {
+ std::ostream& out = GetOutputStream();
+
+ out << "{\n";
+ std::string inner_indent(2, ' ');
+
+ // Open context block and print context information.
+ out << inner_indent << "\"context\": {\n";
+ std::string indent(4, ' ');
+
+ std::string walltime_value = LocalDateTimeString();
+ out << indent << FormatKV("date", walltime_value) << ",\n";
+
+ out << indent << FormatKV("host_name", context.sys_info.name) << ",\n";
+
+ if (Context::executable_name) {
+ out << indent << FormatKV("executable", Context::executable_name) << ",\n";
+ }
+
+ CPUInfo const& info = context.cpu_info;
+ out << indent << FormatKV("num_cpus", static_cast<int64_t>(info.num_cpus))
+ << ",\n";
+ out << indent
+ << FormatKV("mhz_per_cpu",
+ RoundDouble(info.cycles_per_second / 1000000.0))
+ << ",\n";
+ if (CPUInfo::Scaling::UNKNOWN != info.scaling) {
+ out << indent << FormatKV("cpu_scaling_enabled", info.scaling == CPUInfo::Scaling::ENABLED ? true : false)
+ << ",\n";
+ }
+
+ out << indent << "\"caches\": [\n";
+ indent = std::string(6, ' ');
+ std::string cache_indent(8, ' ');
+ for (size_t i = 0; i < info.caches.size(); ++i) {
+ auto& CI = info.caches[i];
+ out << indent << "{\n";
+ out << cache_indent << FormatKV("type", CI.type) << ",\n";
+ out << cache_indent << FormatKV("level", static_cast<int64_t>(CI.level))
+ << ",\n";
+ out << cache_indent
+ << FormatKV("size", static_cast<int64_t>(CI.size)) << ",\n";
+ out << cache_indent
+ << FormatKV("num_sharing", static_cast<int64_t>(CI.num_sharing))
+ << "\n";
+ out << indent << "}";
+ if (i != info.caches.size() - 1) out << ",";
+ out << "\n";
+ }
+ indent = std::string(4, ' ');
+ out << indent << "],\n";
+ out << indent << "\"load_avg\": [";
+ for (auto it = info.load_avg.begin(); it != info.load_avg.end();) {
+ out << *it++;
+ if (it != info.load_avg.end()) out << ",";
+ }
+ out << "],\n";
+
+#if defined(NDEBUG)
+ const char build_type[] = "release";
+#else
+ const char build_type[] = "debug";
+#endif
+ out << indent << FormatKV("library_build_type", build_type) << "\n";
+
+ if (internal::global_context != nullptr) {
+ for (const auto& kv: *internal::global_context) {
+ out << indent << FormatKV(kv.first, kv.second) << "\n";
+ }
+ }
+
+ // Close context block and open the list of benchmarks.
+ out << inner_indent << "},\n";
+ out << inner_indent << "\"benchmarks\": [\n";
+ return true;
+}
+
+void JSONReporter::ReportRuns(std::vector<Run> const& reports) {
+ if (reports.empty()) {
+ return;
+ }
+ std::string indent(4, ' ');
+ std::ostream& out = GetOutputStream();
+ if (!first_report_) {
+ out << ",\n";
+ }
+ first_report_ = false;
+
+ for (auto it = reports.begin(); it != reports.end(); ++it) {
+ out << indent << "{\n";
+ PrintRunData(*it);
+ out << indent << '}';
+ auto it_cp = it;
+ if (++it_cp != reports.end()) {
+ out << ",\n";
+ }
+ }
+}
+
+void JSONReporter::Finalize() {
+ // Close the list of benchmarks and the top level object.
+ GetOutputStream() << "\n ]\n}\n";
+}
+
+void JSONReporter::PrintRunData(Run const& run) {
+ std::string indent(6, ' ');
+ std::ostream& out = GetOutputStream();
+ out << indent << FormatKV("name", run.benchmark_name()) << ",\n";
+ out << indent << FormatKV("family_index", run.family_index) << ",\n";
+ out << indent
+ << FormatKV("per_family_instance_index", run.per_family_instance_index)
+ << ",\n";
+ out << indent << FormatKV("run_name", run.run_name.str()) << ",\n";
+ out << indent << FormatKV("run_type", [&run]() -> const char* {
+ switch (run.run_type) {
+ case BenchmarkReporter::Run::RT_Iteration:
+ return "iteration";
+ case BenchmarkReporter::Run::RT_Aggregate:
+ return "aggregate";
+ }
+ BENCHMARK_UNREACHABLE();
+ }()) << ",\n";
+ out << indent << FormatKV("repetitions", run.repetitions) << ",\n";
+ if (run.run_type != BenchmarkReporter::Run::RT_Aggregate) {
+ out << indent << FormatKV("repetition_index", run.repetition_index)
+ << ",\n";
+ }
+ out << indent << FormatKV("threads", run.threads) << ",\n";
+ if (run.run_type == BenchmarkReporter::Run::RT_Aggregate) {
+ out << indent << FormatKV("aggregate_name", run.aggregate_name) << ",\n";
+ }
+ if (run.error_occurred) {
+ out << indent << FormatKV("error_occurred", run.error_occurred) << ",\n";
+ out << indent << FormatKV("error_message", run.error_message) << ",\n";
+ }
+ if (!run.report_big_o && !run.report_rms) {
+ out << indent << FormatKV("iterations", run.iterations) << ",\n";
+ out << indent << FormatKV("real_time", run.GetAdjustedRealTime()) << ",\n";
+ out << indent << FormatKV("cpu_time", run.GetAdjustedCPUTime());
+ out << ",\n"
+ << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
+ } else if (run.report_big_o) {
+ out << indent << FormatKV("cpu_coefficient", run.GetAdjustedCPUTime())
+ << ",\n";
+ out << indent << FormatKV("real_coefficient", run.GetAdjustedRealTime())
+ << ",\n";
+ out << indent << FormatKV("big_o", GetBigOString(run.complexity)) << ",\n";
+ out << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
+ } else if (run.report_rms) {
+ out << indent << FormatKV("rms", run.GetAdjustedCPUTime());
+ }
+
+ for (auto& c : run.counters) {
+ out << ",\n" << indent << FormatKV(c.first, c.second);
+ }
+
+ if (run.has_memory_result) {
+ out << ",\n" << indent << FormatKV("allocs_per_iter", run.allocs_per_iter);
+ out << ",\n" << indent << FormatKV("max_bytes_used", run.max_bytes_used);
+ }
+
+ if (!run.report_label.empty()) {
+ out << ",\n" << indent << FormatKV("label", run.report_label);
+ }
+ out << '\n';
+}
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_LOG_H_
+#define BENCHMARK_LOG_H_
+
+#include <iostream>
+#include <ostream>
+
+#include "benchmark/benchmark.h"
+
+namespace benchmark {
+namespace internal {
+
+typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
+
+class LogType {
+ friend LogType& GetNullLogInstance();
+ friend LogType& GetErrorLogInstance();
+
+ // FIXME: Add locking to output.
+ template <class Tp>
+ friend LogType& operator<<(LogType&, Tp const&);
+ friend LogType& operator<<(LogType&, EndLType*);
+
+ private:
+ LogType(std::ostream* out) : out_(out) {}
+ std::ostream* out_;
+ BENCHMARK_DISALLOW_COPY_AND_ASSIGN(LogType);
+};
+
+template <class Tp>
+LogType& operator<<(LogType& log, Tp const& value) {
+ if (log.out_) {
+ *log.out_ << value;
+ }
+ return log;
+}
+
+inline LogType& operator<<(LogType& log, EndLType* m) {
+ if (log.out_) {
+ *log.out_ << m;
+ }
+ return log;
+}
+
+inline int& LogLevel() {
+ static int log_level = 0;
+ return log_level;
+}
+
+inline LogType& GetNullLogInstance() {
+ static LogType log(nullptr);
+ return log;
+}
+
+inline LogType& GetErrorLogInstance() {
+ static LogType log(&std::clog);
+ return log;
+}
+
+inline LogType& GetLogInstanceForLevel(int level) {
+ if (level <= LogLevel()) {
+ return GetErrorLogInstance();
+ }
+ return GetNullLogInstance();
+}
+
+} // end namespace internal
+} // end namespace benchmark
+
+// clang-format off
+#define VLOG(x) \
+ (::benchmark::internal::GetLogInstanceForLevel(x) << "-- LOG(" << x << "):" \
+ " ")
+// clang-format on
+#endif
--- /dev/null
+#ifndef BENCHMARK_MUTEX_H_
+#define BENCHMARK_MUTEX_H_
+
+#include <condition_variable>
+#include <mutex>
+
+#include "check.h"
+
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(HAVE_THREAD_SAFETY_ATTRIBUTES)
+#define THREAD_ANNOTATION_ATTRIBUTE_(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE_(x) // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE_(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE_(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE_(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE_(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE_(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE_(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE_(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE_(no_thread_safety_analysis)
+
+namespace benchmark {
+
+typedef std::condition_variable Condition;
+
+// NOTE: Wrappers for std::mutex and std::unique_lock are provided so that
+// we can annotate them with thread safety attributes and use the
+// -Wthread-safety warning with clang. The standard library types cannot be
+// used directly because they do not provide the required annotations.
+class CAPABILITY("mutex") Mutex {
+ public:
+ Mutex() {}
+
+ void lock() ACQUIRE() { mut_.lock(); }
+ void unlock() RELEASE() { mut_.unlock(); }
+ std::mutex& native_handle() { return mut_; }
+
+ private:
+ std::mutex mut_;
+};
+
+class SCOPED_CAPABILITY MutexLock {
+ typedef std::unique_lock<std::mutex> MutexLockImp;
+
+ public:
+ MutexLock(Mutex& m) ACQUIRE(m) : ml_(m.native_handle()) {}
+ ~MutexLock() RELEASE() {}
+ MutexLockImp& native_handle() { return ml_; }
+
+ private:
+ MutexLockImp ml_;
+};
+
+class Barrier {
+ public:
+ Barrier(int num_threads) : running_threads_(num_threads) {}
+
+ // Called by each thread
+ bool wait() EXCLUDES(lock_) {
+ bool last_thread = false;
+ {
+ MutexLock ml(lock_);
+ last_thread = createBarrier(ml);
+ }
+ if (last_thread) phase_condition_.notify_all();
+ return last_thread;
+ }
+
+ void removeThread() EXCLUDES(lock_) {
+ MutexLock ml(lock_);
+ --running_threads_;
+ if (entered_ != 0) phase_condition_.notify_all();
+ }
+
+ private:
+ Mutex lock_;
+ Condition phase_condition_;
+ int running_threads_;
+
+ // State for barrier management
+ int phase_number_ = 0;
+ int entered_ = 0; // Number of threads that have entered this barrier
+
+ // Enter the barrier and wait until all other threads have also
+ // entered the barrier. Returns iff this is the last thread to
+ // enter the barrier.
+ bool createBarrier(MutexLock& ml) REQUIRES(lock_) {
+ CHECK_LT(entered_, running_threads_);
+ entered_++;
+ if (entered_ < running_threads_) {
+ // Wait for all threads to enter
+ int phase_number_cp = phase_number_;
+ auto cb = [this, phase_number_cp]() {
+ return this->phase_number_ > phase_number_cp ||
+ entered_ == running_threads_; // A thread has aborted in error
+ };
+ phase_condition_.wait(ml.native_handle(), cb);
+ if (phase_number_ > phase_number_cp) return false;
+ // else (running_threads_ == entered_) and we are the last thread.
+ }
+ // Last thread has reached the barrier
+ phase_number_++;
+ entered_ = 0;
+ return true;
+ }
+};
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_MUTEX_H_
--- /dev/null
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "perf_counters.h"
+
+#include <cstring>
+#include <vector>
+
+#if defined HAVE_LIBPFM
+#include "perfmon/pfmlib.h"
+#include "perfmon/pfmlib_perf_event.h"
+#endif
+
+namespace benchmark {
+namespace internal {
+
+constexpr size_t PerfCounterValues::kMaxCounters;
+
+#if defined HAVE_LIBPFM
+const bool PerfCounters::kSupported = true;
+
+bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS; }
+
+PerfCounters PerfCounters::Create(
+ const std::vector<std::string>& counter_names) {
+ if (counter_names.empty()) {
+ return NoCounters();
+ }
+ if (counter_names.size() > PerfCounterValues::kMaxCounters) {
+ GetErrorLogInstance()
+ << counter_names.size()
+ << " counters were requested. The minimum is 1, the maximum is "
+ << PerfCounterValues::kMaxCounters << "\n";
+ return NoCounters();
+ }
+ std::vector<int> counter_ids(counter_names.size());
+
+ const int mode = PFM_PLM3; // user mode only
+ for (size_t i = 0; i < counter_names.size(); ++i) {
+ const bool is_first = i == 0;
+ struct perf_event_attr attr{};
+ attr.size = sizeof(attr);
+ const int group_id = !is_first ? counter_ids[0] : -1;
+ const auto& name = counter_names[i];
+ if (name.empty()) {
+ GetErrorLogInstance() << "A counter name was the empty string\n";
+ return NoCounters();
+ }
+ pfm_perf_encode_arg_t arg{};
+ arg.attr = &attr;
+
+ const int pfm_get =
+ pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT, &arg);
+ if (pfm_get != PFM_SUCCESS) {
+ GetErrorLogInstance() << "Unknown counter name: " << name << "\n";
+ return NoCounters();
+ }
+ attr.disabled = is_first;
+ // Note: the man page for perf_event_create suggests inerit = true and
+ // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
+ // case.
+ attr.inherit = true;
+ attr.pinned = is_first;
+ attr.exclude_kernel = true;
+ attr.exclude_user = false;
+ attr.exclude_hv = true;
+ // Read all counters in one read.
+ attr.read_format = PERF_FORMAT_GROUP;
+
+ int id = -1;
+ static constexpr size_t kNrOfSyscallRetries = 5;
+ // Retry syscall as it was interrupted often (b/64774091).
+ for (size_t num_retries = 0; num_retries < kNrOfSyscallRetries;
+ ++num_retries) {
+ id = perf_event_open(&attr, 0, -1, group_id, 0);
+ if (id >= 0 || errno != EINTR) {
+ break;
+ }
+ }
+ if (id < 0) {
+ GetErrorLogInstance()
+ << "Failed to get a file descriptor for " << name << "\n";
+ return NoCounters();
+ }
+
+ counter_ids[i] = id;
+ }
+ if (ioctl(counter_ids[0], PERF_EVENT_IOC_ENABLE) != 0) {
+ GetErrorLogInstance() << "Failed to start counters\n";
+ return NoCounters();
+ }
+
+ return PerfCounters(counter_names, std::move(counter_ids));
+}
+
+PerfCounters::~PerfCounters() {
+ if (counter_ids_.empty()) {
+ return;
+ }
+ ioctl(counter_ids_[0], PERF_EVENT_IOC_DISABLE);
+ for (int fd : counter_ids_) {
+ close(fd);
+ }
+}
+#else // defined HAVE_LIBPFM
+const bool PerfCounters::kSupported = false;
+
+bool PerfCounters::Initialize() { return false; }
+
+PerfCounters PerfCounters::Create(
+ const std::vector<std::string>& counter_names) {
+ if (!counter_names.empty()) {
+ GetErrorLogInstance() << "Performance counters not supported.";
+ }
+ return NoCounters();
+}
+
+PerfCounters::~PerfCounters() = default;
+#endif // defined HAVE_LIBPFM
+} // namespace internal
+} // namespace benchmark
--- /dev/null
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BENCHMARK_PERF_COUNTERS_H
+#define BENCHMARK_PERF_COUNTERS_H
+
+#include <array>
+#include <cstdint>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "check.h"
+#include "log.h"
+
+#ifndef BENCHMARK_OS_WINDOWS
+#include <unistd.h>
+#endif
+
+namespace benchmark {
+namespace internal {
+
+// Typically, we can only read a small number of counters. There is also a
+// padding preceding counter values, when reading multiple counters with one
+// syscall (which is desirable). PerfCounterValues abstracts these details.
+// The implementation ensures the storage is inlined, and allows 0-based
+// indexing into the counter values.
+// The object is used in conjunction with a PerfCounters object, by passing it
+// to Snapshot(). The values are populated such that
+// perfCounters->names()[i]'s value is obtained at position i (as given by
+// operator[]) of this object.
+class PerfCounterValues {
+ public:
+ explicit PerfCounterValues(size_t nr_counters) : nr_counters_(nr_counters) {
+ CHECK_LE(nr_counters_, kMaxCounters);
+ }
+
+ uint64_t operator[](size_t pos) const { return values_[kPadding + pos]; }
+
+ static constexpr size_t kMaxCounters = 3;
+
+ private:
+ friend class PerfCounters;
+ // Get the byte buffer in which perf counters can be captured.
+ // This is used by PerfCounters::Read
+ std::pair<char*, size_t> get_data_buffer() {
+ return {reinterpret_cast<char*>(values_.data()),
+ sizeof(uint64_t) * (kPadding + nr_counters_)};
+ }
+
+ static constexpr size_t kPadding = 1;
+ std::array<uint64_t, kPadding + kMaxCounters> values_;
+ const size_t nr_counters_;
+};
+
+// Collect PMU counters. The object, once constructed, is ready to be used by
+// calling read(). PMU counter collection is enabled from the time create() is
+// called, to obtain the object, until the object's destructor is called.
+class PerfCounters final {
+ public:
+ // True iff this platform supports performance counters.
+ static const bool kSupported;
+
+ bool IsValid() const { return is_valid_; }
+ static PerfCounters NoCounters() { return PerfCounters(); }
+
+ ~PerfCounters();
+ PerfCounters(PerfCounters&&) = default;
+ PerfCounters(const PerfCounters&) = delete;
+
+ // Platform-specific implementations may choose to do some library
+ // initialization here.
+ static bool Initialize();
+
+ // Return a PerfCounters object ready to read the counters with the names
+ // specified. The values are user-mode only. The counter name format is
+ // implementation and OS specific.
+ // TODO: once we move to C++-17, this should be a std::optional, and then the
+ // IsValid() boolean can be dropped.
+ static PerfCounters Create(const std::vector<std::string>& counter_names);
+
+ // Take a snapshot of the current value of the counters into the provided
+ // valid PerfCounterValues storage. The values are populated such that:
+ // names()[i]'s value is (*values)[i]
+ BENCHMARK_ALWAYS_INLINE bool Snapshot(PerfCounterValues* values) const {
+#ifndef BENCHMARK_OS_WINDOWS
+ assert(values != nullptr);
+ assert(IsValid());
+ auto buffer = values->get_data_buffer();
+ auto read_bytes = ::read(counter_ids_[0], buffer.first, buffer.second);
+ return static_cast<size_t>(read_bytes) == buffer.second;
+#else
+ (void)values;
+ return false;
+#endif
+ }
+
+ const std::vector<std::string>& names() const { return counter_names_; }
+ size_t num_counters() const { return counter_names_.size(); }
+
+ private:
+ PerfCounters(const std::vector<std::string>& counter_names,
+ std::vector<int>&& counter_ids)
+ : counter_ids_(std::move(counter_ids)),
+ counter_names_(counter_names),
+ is_valid_(true) {}
+ PerfCounters() : is_valid_(false) {}
+
+ std::vector<int> counter_ids_;
+ const std::vector<std::string> counter_names_;
+ const bool is_valid_;
+};
+
+// Typical usage of the above primitives.
+class PerfCountersMeasurement final {
+ public:
+ PerfCountersMeasurement(PerfCounters&& c)
+ : counters_(std::move(c)),
+ start_values_(counters_.IsValid() ? counters_.names().size() : 0),
+ end_values_(counters_.IsValid() ? counters_.names().size() : 0) {}
+
+ bool IsValid() const { return counters_.IsValid(); }
+
+ BENCHMARK_ALWAYS_INLINE void Start() {
+ assert(IsValid());
+ // Tell the compiler to not move instructions above/below where we take
+ // the snapshot.
+ ClobberMemory();
+ counters_.Snapshot(&start_values_);
+ ClobberMemory();
+ }
+
+ BENCHMARK_ALWAYS_INLINE std::vector<std::pair<std::string, double>>
+ StopAndGetMeasurements() {
+ assert(IsValid());
+ // Tell the compiler to not move instructions above/below where we take
+ // the snapshot.
+ ClobberMemory();
+ counters_.Snapshot(&end_values_);
+ ClobberMemory();
+
+ std::vector<std::pair<std::string, double>> ret;
+ for (size_t i = 0; i < counters_.names().size(); ++i) {
+ double measurement = static_cast<double>(end_values_[i]) -
+ static_cast<double>(start_values_[i]);
+ ret.push_back({counters_.names()[i], measurement});
+ }
+ return ret;
+ }
+
+ private:
+ PerfCounters counters_;
+ PerfCounterValues start_values_;
+ PerfCounterValues end_values_;
+};
+
+BENCHMARK_UNUSED static bool perf_init_anchor = PerfCounters::Initialize();
+
+} // namespace internal
+} // namespace benchmark
+
+#endif // BENCHMARK_PERF_COUNTERS_H
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BENCHMARK_RE_H_
+#define BENCHMARK_RE_H_
+
+#include "internal_macros.h"
+
+// clang-format off
+
+#if !defined(HAVE_STD_REGEX) && \
+ !defined(HAVE_GNU_POSIX_REGEX) && \
+ !defined(HAVE_POSIX_REGEX)
+ // No explicit regex selection; detect based on builtin hints.
+ #if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE)
+ #define HAVE_POSIX_REGEX 1
+ #elif __cplusplus >= 199711L
+ #define HAVE_STD_REGEX 1
+ #endif
+#endif
+
+// Prefer C regex libraries when compiling w/o exceptions so that we can
+// correctly report errors.
+#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \
+ defined(BENCHMARK_HAVE_STD_REGEX) && \
+ (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX))
+ #undef HAVE_STD_REGEX
+#endif
+
+#if defined(HAVE_STD_REGEX)
+ #include <regex>
+#elif defined(HAVE_GNU_POSIX_REGEX)
+ #include <gnuregex.h>
+#elif defined(HAVE_POSIX_REGEX)
+ #include <regex.h>
+#else
+#error No regular expression backend was found!
+#endif
+
+// clang-format on
+
+#include <string>
+
+#include "check.h"
+
+namespace benchmark {
+
+// A wrapper around the POSIX regular expression API that provides automatic
+// cleanup
+class Regex {
+ public:
+ Regex() : init_(false) {}
+
+ ~Regex();
+
+ // Compile a regular expression matcher from spec. Returns true on success.
+ //
+ // On failure (and if error is not nullptr), error is populated with a human
+ // readable error message if an error occurs.
+ bool Init(const std::string& spec, std::string* error);
+
+ // Returns whether str matches the compiled regular expression.
+ bool Match(const std::string& str);
+
+ private:
+ bool init_;
+// Underlying regular expression object
+#if defined(HAVE_STD_REGEX)
+ std::regex re_;
+#elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX)
+ regex_t re_;
+#else
+#error No regular expression backend implementation available
+#endif
+};
+
+#if defined(HAVE_STD_REGEX)
+
+inline bool Regex::Init(const std::string& spec, std::string* error) {
+#ifdef BENCHMARK_HAS_NO_EXCEPTIONS
+ ((void)error); // suppress unused warning
+#else
+ try {
+#endif
+ re_ = std::regex(spec, std::regex_constants::extended);
+ init_ = true;
+#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
+}
+catch (const std::regex_error& e) {
+ if (error) {
+ *error = e.what();
+ }
+}
+#endif
+return init_;
+}
+
+inline Regex::~Regex() {}
+
+inline bool Regex::Match(const std::string& str) {
+ if (!init_) {
+ return false;
+ }
+ return std::regex_search(str, re_);
+}
+
+#else
+inline bool Regex::Init(const std::string& spec, std::string* error) {
+ int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB);
+ if (ec != 0) {
+ if (error) {
+ size_t needed = regerror(ec, &re_, nullptr, 0);
+ char* errbuf = new char[needed];
+ regerror(ec, &re_, errbuf, needed);
+
+ // regerror returns the number of bytes necessary to null terminate
+ // the string, so we move that when assigning to error.
+ CHECK_NE(needed, 0);
+ error->assign(errbuf, needed - 1);
+
+ delete[] errbuf;
+ }
+
+ return false;
+ }
+
+ init_ = true;
+ return true;
+}
+
+inline Regex::~Regex() {
+ if (init_) {
+ regfree(&re_);
+ }
+}
+
+inline bool Regex::Match(const std::string& str) {
+ if (!init_) {
+ return false;
+ }
+ return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0;
+}
+#endif
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_RE_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+#include "timers.h"
+
+#include <cstdlib>
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "check.h"
+#include "string_util.h"
+
+namespace benchmark {
+namespace internal {
+extern std::map<std::string, std::string>* global_context;
+}
+
+BenchmarkReporter::BenchmarkReporter()
+ : output_stream_(&std::cout), error_stream_(&std::cerr) {}
+
+BenchmarkReporter::~BenchmarkReporter() {}
+
+void BenchmarkReporter::PrintBasicContext(std::ostream *out,
+ Context const &context) {
+ CHECK(out) << "cannot be null";
+ auto &Out = *out;
+
+ Out << LocalDateTimeString() << "\n";
+
+ if (context.executable_name)
+ Out << "Running " << context.executable_name << "\n";
+
+ const CPUInfo &info = context.cpu_info;
+ Out << "Run on (" << info.num_cpus << " X "
+ << (info.cycles_per_second / 1000000.0) << " MHz CPU "
+ << ((info.num_cpus > 1) ? "s" : "") << ")\n";
+ if (info.caches.size() != 0) {
+ Out << "CPU Caches:\n";
+ for (auto &CInfo : info.caches) {
+ Out << " L" << CInfo.level << " " << CInfo.type << " "
+ << (CInfo.size / 1024) << " KiB";
+ if (CInfo.num_sharing != 0)
+ Out << " (x" << (info.num_cpus / CInfo.num_sharing) << ")";
+ Out << "\n";
+ }
+ }
+ if (!info.load_avg.empty()) {
+ Out << "Load Average: ";
+ for (auto It = info.load_avg.begin(); It != info.load_avg.end();) {
+ Out << StrFormat("%.2f", *It++);
+ if (It != info.load_avg.end()) Out << ", ";
+ }
+ Out << "\n";
+ }
+
+ if (internal::global_context != nullptr) {
+ for (const auto& kv: *internal::global_context) {
+ Out << kv.first << ": " << kv.second << "\n";
+ }
+ }
+
+ if (CPUInfo::Scaling::ENABLED == info.scaling) {
+ Out << "***WARNING*** CPU scaling is enabled, the benchmark "
+ "real time measurements may be noisy and will incur extra "
+ "overhead.\n";
+ }
+
+#ifndef NDEBUG
+ Out << "***WARNING*** Library was built as DEBUG. Timings may be "
+ "affected.\n";
+#endif
+}
+
+// No initializer because it's already initialized to NULL.
+const char *BenchmarkReporter::Context::executable_name;
+
+BenchmarkReporter::Context::Context()
+ : cpu_info(CPUInfo::Get()), sys_info(SystemInfo::Get()) {}
+
+std::string BenchmarkReporter::Run::benchmark_name() const {
+ std::string name = run_name.str();
+ if (run_type == RT_Aggregate) {
+ name += "_" + aggregate_name;
+ }
+ return name;
+}
+
+double BenchmarkReporter::Run::GetAdjustedRealTime() const {
+ double new_time = real_accumulated_time * GetTimeUnitMultiplier(time_unit);
+ if (iterations != 0) new_time /= static_cast<double>(iterations);
+ return new_time;
+}
+
+double BenchmarkReporter::Run::GetAdjustedCPUTime() const {
+ double new_time = cpu_accumulated_time * GetTimeUnitMultiplier(time_unit);
+ if (iterations != 0) new_time /= static_cast<double>(iterations);
+ return new_time;
+}
+
+} // end namespace benchmark
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "sleep.h"
+
+#include <cerrno>
+#include <cstdlib>
+#include <ctime>
+
+#include "internal_macros.h"
+
+#ifdef BENCHMARK_OS_WINDOWS
+#include <windows.h>
+#endif
+
+#ifdef BENCHMARK_OS_ZOS
+#include <unistd.h>
+#endif
+
+namespace benchmark {
+#ifdef BENCHMARK_OS_WINDOWS
+// Window's Sleep takes milliseconds argument.
+void SleepForMilliseconds(int milliseconds) { Sleep(milliseconds); }
+void SleepForSeconds(double seconds) {
+ SleepForMilliseconds(static_cast<int>(kNumMillisPerSecond * seconds));
+}
+#else // BENCHMARK_OS_WINDOWS
+void SleepForMicroseconds(int microseconds) {
+#ifdef BENCHMARK_OS_ZOS
+ // z/OS does not support nanosleep. Instead call sleep() and then usleep() to
+ // sleep for the remaining microseconds because usleep() will fail if its
+ // argument is greater than 1000000.
+ div_t sleepTime = div(microseconds, kNumMicrosPerSecond);
+ int seconds = sleepTime.quot;
+ while (seconds != 0)
+ seconds = sleep(seconds);
+ while (usleep(sleepTime.rem) == -1 && errno == EINTR)
+ ;
+#else
+ struct timespec sleep_time;
+ sleep_time.tv_sec = microseconds / kNumMicrosPerSecond;
+ sleep_time.tv_nsec = (microseconds % kNumMicrosPerSecond) * kNumNanosPerMicro;
+ while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR)
+ ; // Ignore signals and wait for the full interval to elapse.
+#endif
+}
+
+void SleepForMilliseconds(int milliseconds) {
+ SleepForMicroseconds(milliseconds * kNumMicrosPerMilli);
+}
+
+void SleepForSeconds(double seconds) {
+ SleepForMicroseconds(static_cast<int>(seconds * kNumMicrosPerSecond));
+}
+#endif // BENCHMARK_OS_WINDOWS
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_SLEEP_H_
+#define BENCHMARK_SLEEP_H_
+
+namespace benchmark {
+const int kNumMillisPerSecond = 1000;
+const int kNumMicrosPerMilli = 1000;
+const int kNumMicrosPerSecond = kNumMillisPerSecond * 1000;
+const int kNumNanosPerMicro = 1000;
+const int kNumNanosPerSecond = kNumNanosPerMicro * kNumMicrosPerSecond;
+
+void SleepForMilliseconds(int milliseconds);
+void SleepForSeconds(double seconds);
+} // end namespace benchmark
+
+#endif // BENCHMARK_SLEEP_H_
--- /dev/null
+// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
+// Copyright 2017 Roman Lebedev. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "benchmark/benchmark.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+#include <string>
+#include <vector>
+#include "check.h"
+#include "statistics.h"
+
+namespace benchmark {
+
+auto StatisticsSum = [](const std::vector<double>& v) {
+ return std::accumulate(v.begin(), v.end(), 0.0);
+};
+
+double StatisticsMean(const std::vector<double>& v) {
+ if (v.empty()) return 0.0;
+ return StatisticsSum(v) * (1.0 / v.size());
+}
+
+double StatisticsMedian(const std::vector<double>& v) {
+ if (v.size() < 3) return StatisticsMean(v);
+ std::vector<double> copy(v);
+
+ auto center = copy.begin() + v.size() / 2;
+ std::nth_element(copy.begin(), center, copy.end());
+
+ // did we have an odd number of samples?
+ // if yes, then center is the median
+ // it no, then we are looking for the average between center and the value
+ // before
+ if (v.size() % 2 == 1) return *center;
+ auto center2 = copy.begin() + v.size() / 2 - 1;
+ std::nth_element(copy.begin(), center2, copy.end());
+ return (*center + *center2) / 2.0;
+}
+
+// Return the sum of the squares of this sample set
+auto SumSquares = [](const std::vector<double>& v) {
+ return std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
+};
+
+auto Sqr = [](const double dat) { return dat * dat; };
+auto Sqrt = [](const double dat) {
+ // Avoid NaN due to imprecision in the calculations
+ if (dat < 0.0) return 0.0;
+ return std::sqrt(dat);
+};
+
+double StatisticsStdDev(const std::vector<double>& v) {
+ const auto mean = StatisticsMean(v);
+ if (v.empty()) return mean;
+
+ // Sample standard deviation is undefined for n = 1
+ if (v.size() == 1) return 0.0;
+
+ const double avg_squares = SumSquares(v) * (1.0 / v.size());
+ return Sqrt(v.size() / (v.size() - 1.0) * (avg_squares - Sqr(mean)));
+}
+
+std::vector<BenchmarkReporter::Run> ComputeStats(
+ const std::vector<BenchmarkReporter::Run>& reports) {
+ typedef BenchmarkReporter::Run Run;
+ std::vector<Run> results;
+
+ auto error_count =
+ std::count_if(reports.begin(), reports.end(),
+ [](Run const& run) { return run.error_occurred; });
+
+ if (reports.size() - error_count < 2) {
+ // We don't report aggregated data if there was a single run.
+ return results;
+ }
+
+ // Accumulators.
+ std::vector<double> real_accumulated_time_stat;
+ std::vector<double> cpu_accumulated_time_stat;
+
+ real_accumulated_time_stat.reserve(reports.size());
+ cpu_accumulated_time_stat.reserve(reports.size());
+
+ // All repetitions should be run with the same number of iterations so we
+ // can take this information from the first benchmark.
+ const IterationCount run_iterations = reports.front().iterations;
+ // create stats for user counters
+ struct CounterStat {
+ Counter c;
+ std::vector<double> s;
+ };
+ std::map<std::string, CounterStat> counter_stats;
+ for (Run const& r : reports) {
+ for (auto const& cnt : r.counters) {
+ auto it = counter_stats.find(cnt.first);
+ if (it == counter_stats.end()) {
+ counter_stats.insert({cnt.first, {cnt.second, std::vector<double>{}}});
+ it = counter_stats.find(cnt.first);
+ it->second.s.reserve(reports.size());
+ } else {
+ CHECK_EQ(counter_stats[cnt.first].c.flags, cnt.second.flags);
+ }
+ }
+ }
+
+ // Populate the accumulators.
+ for (Run const& run : reports) {
+ CHECK_EQ(reports[0].benchmark_name(), run.benchmark_name());
+ CHECK_EQ(run_iterations, run.iterations);
+ if (run.error_occurred) continue;
+ real_accumulated_time_stat.emplace_back(run.real_accumulated_time);
+ cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time);
+ // user counters
+ for (auto const& cnt : run.counters) {
+ auto it = counter_stats.find(cnt.first);
+ CHECK_NE(it, counter_stats.end());
+ it->second.s.emplace_back(cnt.second);
+ }
+ }
+
+ // Only add label if it is same for all runs
+ std::string report_label = reports[0].report_label;
+ for (std::size_t i = 1; i < reports.size(); i++) {
+ if (reports[i].report_label != report_label) {
+ report_label = "";
+ break;
+ }
+ }
+
+ const double iteration_rescale_factor =
+ double(reports.size()) / double(run_iterations);
+
+ for (const auto& Stat : *reports[0].statistics) {
+ // Get the data from the accumulator to BenchmarkReporter::Run's.
+ Run data;
+ data.run_name = reports[0].run_name;
+ data.family_index = reports[0].family_index;
+ data.per_family_instance_index = reports[0].per_family_instance_index;
+ data.run_type = BenchmarkReporter::Run::RT_Aggregate;
+ data.threads = reports[0].threads;
+ data.repetitions = reports[0].repetitions;
+ data.repetition_index = Run::no_repetition_index;
+ data.aggregate_name = Stat.name_;
+ data.report_label = report_label;
+
+ // It is incorrect to say that an aggregate is computed over
+ // run's iterations, because those iterations already got averaged.
+ // Similarly, if there are N repetitions with 1 iterations each,
+ // an aggregate will be computed over N measurements, not 1.
+ // Thus it is best to simply use the count of separate reports.
+ data.iterations = reports.size();
+
+ data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat);
+ data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat);
+
+ // We will divide these times by data.iterations when reporting, but the
+ // data.iterations is not nessesairly the scale of these measurements,
+ // because in each repetition, these timers are sum over all the iterations.
+ // And if we want to say that the stats are over N repetitions and not
+ // M iterations, we need to multiply these by (N/M).
+ data.real_accumulated_time *= iteration_rescale_factor;
+ data.cpu_accumulated_time *= iteration_rescale_factor;
+
+ data.time_unit = reports[0].time_unit;
+
+ // user counters
+ for (auto const& kv : counter_stats) {
+ // Do *NOT* rescale the custom counters. They are already properly scaled.
+ const auto uc_stat = Stat.compute_(kv.second.s);
+ auto c = Counter(uc_stat, counter_stats[kv.first].c.flags,
+ counter_stats[kv.first].c.oneK);
+ data.counters[kv.first] = c;
+ }
+
+ results.push_back(data);
+ }
+
+ return results;
+}
+
+} // end namespace benchmark
--- /dev/null
+// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
+// Copyright 2017 Roman Lebedev. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STATISTICS_H_
+#define STATISTICS_H_
+
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+namespace benchmark {
+
+// Return a vector containing the mean, median and standard devation information
+// (and any user-specified info) for the specified list of reports. If 'reports'
+// contains less than two non-errored runs an empty vector is returned
+std::vector<BenchmarkReporter::Run> ComputeStats(
+ const std::vector<BenchmarkReporter::Run>& reports);
+
+double StatisticsMean(const std::vector<double>& v);
+double StatisticsMedian(const std::vector<double>& v);
+double StatisticsStdDev(const std::vector<double>& v);
+
+} // end namespace benchmark
+
+#endif // STATISTICS_H_
--- /dev/null
+#include "string_util.h"
+
+#include <array>
+#ifdef BENCHMARK_STL_ANDROID_GNUSTL
+#include <cerrno>
+#endif
+#include <cmath>
+#include <cstdarg>
+#include <cstdio>
+#include <memory>
+#include <sstream>
+
+#include "arraysize.h"
+
+namespace benchmark {
+namespace {
+
+// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
+const char kBigSIUnits[] = "kMGTPEZY";
+// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
+const char kBigIECUnits[] = "KMGTPEZY";
+// milli, micro, nano, pico, femto, atto, zepto, yocto.
+const char kSmallSIUnits[] = "munpfazy";
+
+// We require that all three arrays have the same size.
+static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits),
+ "SI and IEC unit arrays must be the same size");
+static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits),
+ "Small SI and Big SI unit arrays must be the same size");
+
+static const int64_t kUnitsSize = arraysize(kBigSIUnits);
+
+void ToExponentAndMantissa(double val, double thresh, int precision,
+ double one_k, std::string* mantissa,
+ int64_t* exponent) {
+ std::stringstream mantissa_stream;
+
+ if (val < 0) {
+ mantissa_stream << "-";
+ val = -val;
+ }
+
+ // Adjust threshold so that it never excludes things which can't be rendered
+ // in 'precision' digits.
+ const double adjusted_threshold =
+ std::max(thresh, 1.0 / std::pow(10.0, precision));
+ const double big_threshold = adjusted_threshold * one_k;
+ const double small_threshold = adjusted_threshold;
+ // Values in ]simple_threshold,small_threshold[ will be printed as-is
+ const double simple_threshold = 0.01;
+
+ if (val > big_threshold) {
+ // Positive powers
+ double scaled = val;
+ for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) {
+ scaled /= one_k;
+ if (scaled <= big_threshold) {
+ mantissa_stream << scaled;
+ *exponent = i + 1;
+ *mantissa = mantissa_stream.str();
+ return;
+ }
+ }
+ mantissa_stream << val;
+ *exponent = 0;
+ } else if (val < small_threshold) {
+ // Negative powers
+ if (val < simple_threshold) {
+ double scaled = val;
+ for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) {
+ scaled *= one_k;
+ if (scaled >= small_threshold) {
+ mantissa_stream << scaled;
+ *exponent = -static_cast<int64_t>(i + 1);
+ *mantissa = mantissa_stream.str();
+ return;
+ }
+ }
+ }
+ mantissa_stream << val;
+ *exponent = 0;
+ } else {
+ mantissa_stream << val;
+ *exponent = 0;
+ }
+ *mantissa = mantissa_stream.str();
+}
+
+std::string ExponentToPrefix(int64_t exponent, bool iec) {
+ if (exponent == 0) return "";
+
+ const int64_t index = (exponent > 0 ? exponent - 1 : -exponent - 1);
+ if (index >= kUnitsSize) return "";
+
+ const char* array =
+ (exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits);
+ if (iec)
+ return array[index] + std::string("i");
+ else
+ return std::string(1, array[index]);
+}
+
+std::string ToBinaryStringFullySpecified(double value, double threshold,
+ int precision, double one_k = 1024.0) {
+ std::string mantissa;
+ int64_t exponent;
+ ToExponentAndMantissa(value, threshold, precision, one_k, &mantissa,
+ &exponent);
+ return mantissa + ExponentToPrefix(exponent, false);
+}
+
+} // end namespace
+
+void AppendHumanReadable(int n, std::string* str) {
+ std::stringstream ss;
+ // Round down to the nearest SI prefix.
+ ss << ToBinaryStringFullySpecified(n, 1.0, 0);
+ *str += ss.str();
+}
+
+std::string HumanReadableNumber(double n, double one_k) {
+ // 1.1 means that figures up to 1.1k should be shown with the next unit down;
+ // this softens edge effects.
+ // 1 means that we should show one decimal place of precision.
+ return ToBinaryStringFullySpecified(n, 1.1, 1, one_k);
+}
+
+std::string StrFormatImp(const char* msg, va_list args) {
+ // we might need a second shot at this, so pre-emptivly make a copy
+ va_list args_cp;
+ va_copy(args_cp, args);
+
+ // TODO(ericwf): use std::array for first attempt to avoid one memory
+ // allocation guess what the size might be
+ std::array<char, 256> local_buff;
+ std::size_t size = local_buff.size();
+ // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
+ // in the android-ndk
+ auto ret = vsnprintf(local_buff.data(), size, msg, args_cp);
+
+ va_end(args_cp);
+
+ // handle empty expansion
+ if (ret == 0) return std::string{};
+ if (static_cast<std::size_t>(ret) < size)
+ return std::string(local_buff.data());
+
+ // we did not provide a long enough buffer on our first attempt.
+ // add 1 to size to account for null-byte in size cast to prevent overflow
+ size = static_cast<std::size_t>(ret) + 1;
+ auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
+ // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
+ // in the android-ndk
+ ret = vsnprintf(buff_ptr.get(), size, msg, args);
+ return std::string(buff_ptr.get());
+}
+
+std::string StrFormat(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ std::string tmp = StrFormatImp(format, args);
+ va_end(args);
+ return tmp;
+}
+
+std::vector<std::string> StrSplit(const std::string& str, char delim) {
+ if (str.empty()) return {};
+ std::vector<std::string> ret;
+ size_t first = 0;
+ size_t next = str.find(delim);
+ for (; next != std::string::npos;
+ first = next + 1, next = str.find(delim, first)) {
+ ret.push_back(str.substr(first, next - first));
+ }
+ ret.push_back(str.substr(first));
+ return ret;
+}
+
+#ifdef BENCHMARK_STL_ANDROID_GNUSTL
+/*
+ * GNU STL in Android NDK lacks support for some C++11 functions, including
+ * stoul, stoi, stod. We reimplement them here using C functions strtoul,
+ * strtol, strtod. Note that reimplemented functions are in benchmark::
+ * namespace, not std:: namespace.
+ */
+unsigned long stoul(const std::string& str, size_t* pos, int base) {
+ /* Record previous errno */
+ const int oldErrno = errno;
+ errno = 0;
+
+ const char* strStart = str.c_str();
+ char* strEnd = const_cast<char*>(strStart);
+ const unsigned long result = strtoul(strStart, &strEnd, base);
+
+ const int strtoulErrno = errno;
+ /* Restore previous errno */
+ errno = oldErrno;
+
+ /* Check for errors and return */
+ if (strtoulErrno == ERANGE) {
+ throw std::out_of_range(
+ "stoul failed: " + str + " is outside of range of unsigned long");
+ } else if (strEnd == strStart || strtoulErrno != 0) {
+ throw std::invalid_argument(
+ "stoul failed: " + str + " is not an integer");
+ }
+ if (pos != nullptr) {
+ *pos = static_cast<size_t>(strEnd - strStart);
+ }
+ return result;
+}
+
+int stoi(const std::string& str, size_t* pos, int base) {
+ /* Record previous errno */
+ const int oldErrno = errno;
+ errno = 0;
+
+ const char* strStart = str.c_str();
+ char* strEnd = const_cast<char*>(strStart);
+ const long result = strtol(strStart, &strEnd, base);
+
+ const int strtolErrno = errno;
+ /* Restore previous errno */
+ errno = oldErrno;
+
+ /* Check for errors and return */
+ if (strtolErrno == ERANGE || long(int(result)) != result) {
+ throw std::out_of_range(
+ "stoul failed: " + str + " is outside of range of int");
+ } else if (strEnd == strStart || strtolErrno != 0) {
+ throw std::invalid_argument(
+ "stoul failed: " + str + " is not an integer");
+ }
+ if (pos != nullptr) {
+ *pos = static_cast<size_t>(strEnd - strStart);
+ }
+ return int(result);
+}
+
+double stod(const std::string& str, size_t* pos) {
+ /* Record previous errno */
+ const int oldErrno = errno;
+ errno = 0;
+
+ const char* strStart = str.c_str();
+ char* strEnd = const_cast<char*>(strStart);
+ const double result = strtod(strStart, &strEnd);
+
+ /* Restore previous errno */
+ const int strtodErrno = errno;
+ errno = oldErrno;
+
+ /* Check for errors and return */
+ if (strtodErrno == ERANGE) {
+ throw std::out_of_range(
+ "stoul failed: " + str + " is outside of range of int");
+ } else if (strEnd == strStart || strtodErrno != 0) {
+ throw std::invalid_argument(
+ "stoul failed: " + str + " is not an integer");
+ }
+ if (pos != nullptr) {
+ *pos = static_cast<size_t>(strEnd - strStart);
+ }
+ return result;
+}
+#endif
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_STRING_UTIL_H_
+#define BENCHMARK_STRING_UTIL_H_
+
+#include <sstream>
+#include <string>
+#include <utility>
+#include "internal_macros.h"
+
+namespace benchmark {
+
+void AppendHumanReadable(int n, std::string* str);
+
+std::string HumanReadableNumber(double n, double one_k = 1024.0);
+
+#if defined(__MINGW32__)
+__attribute__((format(__MINGW_PRINTF_FORMAT, 1, 2)))
+#elif defined(__GNUC__)
+__attribute__((format(printf, 1, 2)))
+#endif
+std::string
+StrFormat(const char* format, ...);
+
+inline std::ostream& StrCatImp(std::ostream& out) BENCHMARK_NOEXCEPT {
+ return out;
+}
+
+template <class First, class... Rest>
+inline std::ostream& StrCatImp(std::ostream& out, First&& f, Rest&&... rest) {
+ out << std::forward<First>(f);
+ return StrCatImp(out, std::forward<Rest>(rest)...);
+}
+
+template <class... Args>
+inline std::string StrCat(Args&&... args) {
+ std::ostringstream ss;
+ StrCatImp(ss, std::forward<Args>(args)...);
+ return ss.str();
+}
+
+std::vector<std::string> StrSplit(const std::string& str, char delim);
+
+#ifdef BENCHMARK_STL_ANDROID_GNUSTL
+/*
+ * GNU STL in Android NDK lacks support for some C++11 functions, including
+ * stoul, stoi, stod. We reimplement them here using C functions strtoul,
+ * strtol, strtod. Note that reimplemented functions are in benchmark::
+ * namespace, not std:: namespace.
+ */
+unsigned long stoul(const std::string& str, size_t* pos = nullptr,
+ int base = 10);
+int stoi(const std::string& str, size_t* pos = nullptr, int base = 10);
+double stod(const std::string& str, size_t* pos = nullptr);
+#else
+using std::stoul;
+using std::stoi;
+using std::stod;
+#endif
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_STRING_UTIL_H_
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "internal_macros.h"
+
+#ifdef BENCHMARK_OS_WINDOWS
+#include <shlwapi.h>
+#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
+#include <versionhelpers.h>
+#include <windows.h>
+#include <codecvt>
+#else
+#include <fcntl.h>
+#ifndef BENCHMARK_OS_FUCHSIA
+#include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
+#include <unistd.h>
+#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
+ defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
+ defined BENCHMARK_OS_DRAGONFLY
+#define BENCHMARK_HAS_SYSCTL
+#include <sys/sysctl.h>
+#endif
+#endif
+#if defined(BENCHMARK_OS_SOLARIS)
+#include <kstat.h>
+#endif
+#if defined(BENCHMARK_OS_QNX)
+#include <sys/syspage.h>
+#endif
+
+#include <algorithm>
+#include <array>
+#include <bitset>
+#include <cerrno>
+#include <climits>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <sstream>
+#include <locale>
+#include <utility>
+
+#include "check.h"
+#include "cycleclock.h"
+#include "internal_macros.h"
+#include "log.h"
+#include "sleep.h"
+#include "string_util.h"
+
+namespace benchmark {
+namespace {
+
+void PrintImp(std::ostream& out) { out << std::endl; }
+
+template <class First, class... Rest>
+void PrintImp(std::ostream& out, First&& f, Rest&&... rest) {
+ out << std::forward<First>(f);
+ PrintImp(out, std::forward<Rest>(rest)...);
+}
+
+template <class... Args>
+BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) {
+ PrintImp(std::cerr, std::forward<Args>(args)...);
+ std::exit(EXIT_FAILURE);
+}
+
+#ifdef BENCHMARK_HAS_SYSCTL
+
+/// ValueUnion - A type used to correctly alias the byte-for-byte output of
+/// `sysctl` with the result type it's to be interpreted as.
+struct ValueUnion {
+ union DataT {
+ uint32_t uint32_value;
+ uint64_t uint64_value;
+ // For correct aliasing of union members from bytes.
+ char bytes[8];
+ };
+ using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;
+
+ // The size of the data union member + its trailing array size.
+ size_t Size;
+ DataPtr Buff;
+
+ public:
+ ValueUnion() : Size(0), Buff(nullptr, &std::free) {}
+
+ explicit ValueUnion(size_t BuffSize)
+ : Size(sizeof(DataT) + BuffSize),
+ Buff(::new (std::malloc(Size)) DataT(), &std::free) {}
+
+ ValueUnion(ValueUnion&& other) = default;
+
+ explicit operator bool() const { return bool(Buff); }
+
+ char* data() const { return Buff->bytes; }
+
+ std::string GetAsString() const { return std::string(data()); }
+
+ int64_t GetAsInteger() const {
+ if (Size == sizeof(Buff->uint32_value))
+ return static_cast<int32_t>(Buff->uint32_value);
+ else if (Size == sizeof(Buff->uint64_value))
+ return static_cast<int64_t>(Buff->uint64_value);
+ BENCHMARK_UNREACHABLE();
+ }
+
+ uint64_t GetAsUnsigned() const {
+ if (Size == sizeof(Buff->uint32_value))
+ return Buff->uint32_value;
+ else if (Size == sizeof(Buff->uint64_value))
+ return Buff->uint64_value;
+ BENCHMARK_UNREACHABLE();
+ }
+
+ template <class T, int N>
+ std::array<T, N> GetAsArray() {
+ const int ArrSize = sizeof(T) * N;
+ CHECK_LE(ArrSize, Size);
+ std::array<T, N> Arr;
+ std::memcpy(Arr.data(), data(), ArrSize);
+ return Arr;
+ }
+};
+
+ValueUnion GetSysctlImp(std::string const& Name) {
+#if defined BENCHMARK_OS_OPENBSD
+ int mib[2];
+
+ mib[0] = CTL_HW;
+ if ((Name == "hw.ncpu") || (Name == "hw.cpuspeed")){
+ ValueUnion buff(sizeof(int));
+
+ if (Name == "hw.ncpu") {
+ mib[1] = HW_NCPU;
+ } else {
+ mib[1] = HW_CPUSPEED;
+ }
+
+ if (sysctl(mib, 2, buff.data(), &buff.Size, nullptr, 0) == -1) {
+ return ValueUnion();
+ }
+ return buff;
+ }
+ return ValueUnion();
+#else
+ size_t CurBuffSize = 0;
+ if (sysctlbyname(Name.c_str(), nullptr, &CurBuffSize, nullptr, 0) == -1)
+ return ValueUnion();
+
+ ValueUnion buff(CurBuffSize);
+ if (sysctlbyname(Name.c_str(), buff.data(), &buff.Size, nullptr, 0) == 0)
+ return buff;
+ return ValueUnion();
+#endif
+}
+
+BENCHMARK_MAYBE_UNUSED
+bool GetSysctl(std::string const& Name, std::string* Out) {
+ Out->clear();
+ auto Buff = GetSysctlImp(Name);
+ if (!Buff) return false;
+ Out->assign(Buff.data());
+ return true;
+}
+
+template <class Tp,
+ class = typename std::enable_if<std::is_integral<Tp>::value>::type>
+bool GetSysctl(std::string const& Name, Tp* Out) {
+ *Out = 0;
+ auto Buff = GetSysctlImp(Name);
+ if (!Buff) return false;
+ *Out = static_cast<Tp>(Buff.GetAsUnsigned());
+ return true;
+}
+
+template <class Tp, size_t N>
+bool GetSysctl(std::string const& Name, std::array<Tp, N>* Out) {
+ auto Buff = GetSysctlImp(Name);
+ if (!Buff) return false;
+ *Out = Buff.GetAsArray<Tp, N>();
+ return true;
+}
+#endif
+
+template <class ArgT>
+bool ReadFromFile(std::string const& fname, ArgT* arg) {
+ *arg = ArgT();
+ std::ifstream f(fname.c_str());
+ if (!f.is_open()) return false;
+ f >> *arg;
+ return f.good();
+}
+
+CPUInfo::Scaling CpuScaling(int num_cpus) {
+ // We don't have a valid CPU count, so don't even bother.
+ if (num_cpus <= 0) return CPUInfo::Scaling::UNKNOWN;
+#ifdef BENCHMARK_OS_QNX
+ return CPUInfo::Scaling::UNKNOWN;
+#endif
+#ifndef BENCHMARK_OS_WINDOWS
+ // On Linux, the CPUfreq subsystem exposes CPU information as files on the
+ // local file system. If reading the exported files fails, then we may not be
+ // running on Linux, so we silently ignore all the read errors.
+ std::string res;
+ for (int cpu = 0; cpu < num_cpus; ++cpu) {
+ std::string governor_file =
+ StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
+ if (ReadFromFile(governor_file, &res) && res != "performance") return CPUInfo::Scaling::ENABLED;
+ }
+ return CPUInfo::Scaling::DISABLED;
+#endif
+ return CPUInfo::Scaling::UNKNOWN;
+}
+
+int CountSetBitsInCPUMap(std::string Val) {
+ auto CountBits = [](std::string Part) {
+ using CPUMask = std::bitset<sizeof(std::uintptr_t) * CHAR_BIT>;
+ Part = "0x" + Part;
+ CPUMask Mask(benchmark::stoul(Part, nullptr, 16));
+ return static_cast<int>(Mask.count());
+ };
+ size_t Pos;
+ int total = 0;
+ while ((Pos = Val.find(',')) != std::string::npos) {
+ total += CountBits(Val.substr(0, Pos));
+ Val = Val.substr(Pos + 1);
+ }
+ if (!Val.empty()) {
+ total += CountBits(Val);
+ }
+ return total;
+}
+
+BENCHMARK_MAYBE_UNUSED
+std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() {
+ std::vector<CPUInfo::CacheInfo> res;
+ std::string dir = "/sys/devices/system/cpu/cpu0/cache/";
+ int Idx = 0;
+ while (true) {
+ CPUInfo::CacheInfo info;
+ std::string FPath = StrCat(dir, "index", Idx++, "/");
+ std::ifstream f(StrCat(FPath, "size").c_str());
+ if (!f.is_open()) break;
+ std::string suffix;
+ f >> info.size;
+ if (f.fail())
+ PrintErrorAndDie("Failed while reading file '", FPath, "size'");
+ if (f.good()) {
+ f >> suffix;
+ if (f.bad())
+ PrintErrorAndDie(
+ "Invalid cache size format: failed to read size suffix");
+ else if (f && suffix != "K")
+ PrintErrorAndDie("Invalid cache size format: Expected bytes ", suffix);
+ else if (suffix == "K")
+ info.size *= 1024;
+ }
+ if (!ReadFromFile(StrCat(FPath, "type"), &info.type))
+ PrintErrorAndDie("Failed to read from file ", FPath, "type");
+ if (!ReadFromFile(StrCat(FPath, "level"), &info.level))
+ PrintErrorAndDie("Failed to read from file ", FPath, "level");
+ std::string map_str;
+ if (!ReadFromFile(StrCat(FPath, "shared_cpu_map"), &map_str))
+ PrintErrorAndDie("Failed to read from file ", FPath, "shared_cpu_map");
+ info.num_sharing = CountSetBitsInCPUMap(map_str);
+ res.push_back(info);
+ }
+
+ return res;
+}
+
+#ifdef BENCHMARK_OS_MACOSX
+std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
+ std::vector<CPUInfo::CacheInfo> res;
+ std::array<uint64_t, 4> CacheCounts{{0, 0, 0, 0}};
+ GetSysctl("hw.cacheconfig", &CacheCounts);
+
+ struct {
+ std::string name;
+ std::string type;
+ int level;
+ uint64_t num_sharing;
+ } Cases[] = {{"hw.l1dcachesize", "Data", 1, CacheCounts[1]},
+ {"hw.l1icachesize", "Instruction", 1, CacheCounts[1]},
+ {"hw.l2cachesize", "Unified", 2, CacheCounts[2]},
+ {"hw.l3cachesize", "Unified", 3, CacheCounts[3]}};
+ for (auto& C : Cases) {
+ int val;
+ if (!GetSysctl(C.name, &val)) continue;
+ CPUInfo::CacheInfo info;
+ info.type = C.type;
+ info.level = C.level;
+ info.size = val;
+ info.num_sharing = static_cast<int>(C.num_sharing);
+ res.push_back(std::move(info));
+ }
+ return res;
+}
+#elif defined(BENCHMARK_OS_WINDOWS)
+std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
+ std::vector<CPUInfo::CacheInfo> res;
+ DWORD buffer_size = 0;
+ using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
+ using CInfo = CACHE_DESCRIPTOR;
+
+ using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
+ GetLogicalProcessorInformation(nullptr, &buffer_size);
+ UPtr buff((PInfo*)malloc(buffer_size), &std::free);
+ if (!GetLogicalProcessorInformation(buff.get(), &buffer_size))
+ PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ",
+ GetLastError());
+
+ PInfo* it = buff.get();
+ PInfo* end = buff.get() + (buffer_size / sizeof(PInfo));
+
+ for (; it != end; ++it) {
+ if (it->Relationship != RelationCache) continue;
+ using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>;
+ BitSet B(it->ProcessorMask);
+ // To prevent duplicates, only consider caches where CPU 0 is specified
+ if (!B.test(0)) continue;
+ CInfo* Cache = &it->Cache;
+ CPUInfo::CacheInfo C;
+ C.num_sharing = static_cast<int>(B.count());
+ C.level = Cache->Level;
+ C.size = Cache->Size;
+ switch (Cache->Type) {
+ case CacheUnified:
+ C.type = "Unified";
+ break;
+ case CacheInstruction:
+ C.type = "Instruction";
+ break;
+ case CacheData:
+ C.type = "Data";
+ break;
+ case CacheTrace:
+ C.type = "Trace";
+ break;
+ default:
+ C.type = "Unknown";
+ break;
+ }
+ res.push_back(C);
+ }
+ return res;
+}
+#elif BENCHMARK_OS_QNX
+std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
+ std::vector<CPUInfo::CacheInfo> res;
+ struct cacheattr_entry *cache = SYSPAGE_ENTRY(cacheattr);
+ uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
+ int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize ;
+ for(int i = 0; i < num; ++i ) {
+ CPUInfo::CacheInfo info;
+ switch (cache->flags){
+ case CACHE_FLAG_INSTR :
+ info.type = "Instruction";
+ info.level = 1;
+ break;
+ case CACHE_FLAG_DATA :
+ info.type = "Data";
+ info.level = 1;
+ break;
+ case CACHE_FLAG_UNIFIED :
+ info.type = "Unified";
+ info.level = 2;
+ break;
+ case CACHE_FLAG_SHARED :
+ info.type = "Shared";
+ info.level = 3;
+ break;
+ default :
+ continue;
+ break;
+ }
+ info.size = cache->line_size * cache->num_lines;
+ info.num_sharing = 0;
+ res.push_back(std::move(info));
+ cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
+ }
+ return res;
+}
+#endif
+
+std::vector<CPUInfo::CacheInfo> GetCacheSizes() {
+#ifdef BENCHMARK_OS_MACOSX
+ return GetCacheSizesMacOSX();
+#elif defined(BENCHMARK_OS_WINDOWS)
+ return GetCacheSizesWindows();
+#elif defined(BENCHMARK_OS_QNX)
+ return GetCacheSizesQNX();
+#else
+ return GetCacheSizesFromKVFS();
+#endif
+}
+
+std::string GetSystemName() {
+#if defined(BENCHMARK_OS_WINDOWS)
+ std::string str;
+ const unsigned COUNT = MAX_COMPUTERNAME_LENGTH+1;
+ TCHAR hostname[COUNT] = {'\0'};
+ DWORD DWCOUNT = COUNT;
+ if (!GetComputerName(hostname, &DWCOUNT))
+ return std::string("");
+#ifndef UNICODE
+ str = std::string(hostname, DWCOUNT);
+#else
+ //Using wstring_convert, Is deprecated in C++17
+ using convert_type = std::codecvt_utf8<wchar_t>;
+ std::wstring_convert<convert_type, wchar_t> converter;
+ std::wstring wStr(hostname, DWCOUNT);
+ str = converter.to_bytes(wStr);
+#endif
+ return str;
+#else // defined(BENCHMARK_OS_WINDOWS)
+#ifndef HOST_NAME_MAX
+#ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac Doesnt have HOST_NAME_MAX defined
+#define HOST_NAME_MAX 64
+#elif defined(BENCHMARK_OS_NACL)
+#define HOST_NAME_MAX 64
+#elif defined(BENCHMARK_OS_QNX)
+#define HOST_NAME_MAX 154
+#elif defined(BENCHMARK_OS_RTEMS)
+#define HOST_NAME_MAX 256
+#else
+#warning "HOST_NAME_MAX not defined. using 64"
+#define HOST_NAME_MAX 64
+#endif
+#endif // def HOST_NAME_MAX
+ char hostname[HOST_NAME_MAX];
+ int retVal = gethostname(hostname, HOST_NAME_MAX);
+ if (retVal != 0) return std::string("");
+ return std::string(hostname);
+#endif // Catch-all POSIX block.
+}
+
+int GetNumCPUs() {
+#ifdef BENCHMARK_HAS_SYSCTL
+ int NumCPU = -1;
+ if (GetSysctl("hw.ncpu", &NumCPU)) return NumCPU;
+ fprintf(stderr, "Err: %s\n", strerror(errno));
+ std::exit(EXIT_FAILURE);
+#elif defined(BENCHMARK_OS_WINDOWS)
+ SYSTEM_INFO sysinfo;
+ // Use memset as opposed to = {} to avoid GCC missing initializer false
+ // positives.
+ std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors; // number of logical
+ // processors in the current
+ // group
+#elif defined(BENCHMARK_OS_SOLARIS)
+ // Returns -1 in case of a failure.
+ int NumCPU = sysconf(_SC_NPROCESSORS_ONLN);
+ if (NumCPU < 0) {
+ fprintf(stderr,
+ "sysconf(_SC_NPROCESSORS_ONLN) failed with error: %s\n",
+ strerror(errno));
+ }
+ return NumCPU;
+#elif defined(BENCHMARK_OS_QNX)
+ return static_cast<int>(_syspage_ptr->num_cpu);
+#else
+ int NumCPUs = 0;
+ int MaxID = -1;
+ std::ifstream f("/proc/cpuinfo");
+ if (!f.is_open()) {
+ std::cerr << "failed to open /proc/cpuinfo\n";
+ return -1;
+ }
+ const std::string Key = "processor";
+ std::string ln;
+ while (std::getline(f, ln)) {
+ if (ln.empty()) continue;
+ size_t SplitIdx = ln.find(':');
+ std::string value;
+#if defined(__s390__)
+ // s390 has another format in /proc/cpuinfo
+ // it needs to be parsed differently
+ if (SplitIdx != std::string::npos) value = ln.substr(Key.size()+1,SplitIdx-Key.size()-1);
+#else
+ if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1);
+#endif
+ if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) {
+ NumCPUs++;
+ if (!value.empty()) {
+ int CurID = benchmark::stoi(value);
+ MaxID = std::max(CurID, MaxID);
+ }
+ }
+ }
+ if (f.bad()) {
+ std::cerr << "Failure reading /proc/cpuinfo\n";
+ return -1;
+ }
+ if (!f.eof()) {
+ std::cerr << "Failed to read to end of /proc/cpuinfo\n";
+ return -1;
+ }
+ f.close();
+
+ if ((MaxID + 1) != NumCPUs) {
+ fprintf(stderr,
+ "CPU ID assignments in /proc/cpuinfo seem messed up."
+ " This is usually caused by a bad BIOS.\n");
+ }
+ return NumCPUs;
+#endif
+ BENCHMARK_UNREACHABLE();
+}
+
+double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) {
+ // Currently, scaling is only used on linux path here,
+ // suppress diagnostics about it being unused on other paths.
+ (void)scaling;
+
+#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
+ long freq;
+
+ // If the kernel is exporting the tsc frequency use that. There are issues
+ // where cpuinfo_max_freq cannot be relied on because the BIOS may be
+ // exporintg an invalid p-state (on x86) or p-states may be used to put the
+ // processor in a new mode (turbo mode). Essentially, those frequencies
+ // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
+ // well.
+ if (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)
+ // If CPU scaling is disabled, use the the *current* frequency.
+ // Note that we specifically don't want to read cpuinfo_cur_freq,
+ // because it is only readable by root.
+ || (scaling == CPUInfo::Scaling::DISABLED &&
+ ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
+ &freq))
+ // Otherwise, if CPU scaling may be in effect, we want to use
+ // the *maximum* frequency, not whatever CPU speed some random processor
+ // happens to be using now.
+ || ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
+ &freq)) {
+ // The value is in kHz (as the file name suggests). For example, on a
+ // 2GHz warpstation, the file contains the value "2000000".
+ return freq * 1000.0;
+ }
+
+ const double error_value = -1;
+ double bogo_clock = error_value;
+
+ std::ifstream f("/proc/cpuinfo");
+ if (!f.is_open()) {
+ std::cerr << "failed to open /proc/cpuinfo\n";
+ return error_value;
+ }
+
+ auto startsWithKey = [](std::string const& Value, std::string const& Key) {
+ if (Key.size() > Value.size()) return false;
+ auto Cmp = [&](char X, char Y) {
+ return std::tolower(X) == std::tolower(Y);
+ };
+ return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp);
+ };
+
+ std::string ln;
+ while (std::getline(f, ln)) {
+ if (ln.empty()) continue;
+ size_t SplitIdx = ln.find(':');
+ std::string value;
+ if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1);
+ // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
+ // accept positive values. Some environments (virtual machines) report zero,
+ // which would cause infinite looping in WallTime_Init.
+ if (startsWithKey(ln, "cpu MHz")) {
+ if (!value.empty()) {
+ double cycles_per_second = benchmark::stod(value) * 1000000.0;
+ if (cycles_per_second > 0) return cycles_per_second;
+ }
+ } else if (startsWithKey(ln, "bogomips")) {
+ if (!value.empty()) {
+ bogo_clock = benchmark::stod(value) * 1000000.0;
+ if (bogo_clock < 0.0) bogo_clock = error_value;
+ }
+ }
+ }
+ if (f.bad()) {
+ std::cerr << "Failure reading /proc/cpuinfo\n";
+ return error_value;
+ }
+ if (!f.eof()) {
+ std::cerr << "Failed to read to end of /proc/cpuinfo\n";
+ return error_value;
+ }
+ f.close();
+ // If we found the bogomips clock, but nothing better, we'll use it (but
+ // we're not happy about it); otherwise, fallback to the rough estimation
+ // below.
+ if (bogo_clock >= 0.0) return bogo_clock;
+
+#elif defined BENCHMARK_HAS_SYSCTL
+ constexpr auto* FreqStr =
+#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
+ "machdep.tsc_freq";
+#elif defined BENCHMARK_OS_OPENBSD
+ "hw.cpuspeed";
+#elif defined BENCHMARK_OS_DRAGONFLY
+ "hw.tsc_frequency";
+#else
+ "hw.cpufrequency";
+#endif
+ unsigned long long hz = 0;
+#if defined BENCHMARK_OS_OPENBSD
+ if (GetSysctl(FreqStr, &hz)) return hz * 1000000;
+#else
+ if (GetSysctl(FreqStr, &hz)) return hz;
+#endif
+ fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
+ FreqStr, strerror(errno));
+
+#elif defined BENCHMARK_OS_WINDOWS
+ // In NT, read MHz from the registry. If we fail to do so or we're in win9x
+ // then make a crude estimate.
+ DWORD data, data_size = sizeof(data);
+ if (IsWindowsXPOrGreater() &&
+ SUCCEEDED(
+ SHGetValueA(HKEY_LOCAL_MACHINE,
+ "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+ "~MHz", nullptr, &data, &data_size)))
+ return static_cast<double>((int64_t)data *
+ (int64_t)(1000 * 1000)); // was mhz
+#elif defined (BENCHMARK_OS_SOLARIS)
+ kstat_ctl_t *kc = kstat_open();
+ if (!kc) {
+ std::cerr << "failed to open /dev/kstat\n";
+ return -1;
+ }
+ kstat_t *ksp = kstat_lookup(kc, (char*)"cpu_info", -1, (char*)"cpu_info0");
+ if (!ksp) {
+ std::cerr << "failed to lookup in /dev/kstat\n";
+ return -1;
+ }
+ if (kstat_read(kc, ksp, NULL) < 0) {
+ std::cerr << "failed to read from /dev/kstat\n";
+ return -1;
+ }
+ kstat_named_t *knp =
+ (kstat_named_t*)kstat_data_lookup(ksp, (char*)"current_clock_Hz");
+ if (!knp) {
+ std::cerr << "failed to lookup data in /dev/kstat\n";
+ return -1;
+ }
+ if (knp->data_type != KSTAT_DATA_UINT64) {
+ std::cerr << "current_clock_Hz is of unexpected data type: "
+ << knp->data_type << "\n";
+ return -1;
+ }
+ double clock_hz = knp->value.ui64;
+ kstat_close(kc);
+ return clock_hz;
+#elif defined (BENCHMARK_OS_QNX)
+ return static_cast<double>((int64_t)(SYSPAGE_ENTRY(cpuinfo)->speed) *
+ (int64_t)(1000 * 1000));
+#endif
+ // If we've fallen through, attempt to roughly estimate the CPU clock rate.
+ const int estimate_time_ms = 1000;
+ const auto start_ticks = cycleclock::Now();
+ SleepForMilliseconds(estimate_time_ms);
+ return static_cast<double>(cycleclock::Now() - start_ticks);
+}
+
+std::vector<double> GetLoadAvg() {
+#if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \
+ defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \
+ defined BENCHMARK_OS_OPENBSD || defined BENCHMARK_OS_DRAGONFLY) && \
+ !defined(__ANDROID__)
+ constexpr int kMaxSamples = 3;
+ std::vector<double> res(kMaxSamples, 0.0);
+ const int nelem = getloadavg(res.data(), kMaxSamples);
+ if (nelem < 1) {
+ res.clear();
+ } else {
+ res.resize(nelem);
+ }
+ return res;
+#else
+ return {};
+#endif
+}
+
+} // end namespace
+
+const CPUInfo& CPUInfo::Get() {
+ static const CPUInfo* info = new CPUInfo();
+ return *info;
+}
+
+CPUInfo::CPUInfo()
+ : num_cpus(GetNumCPUs()),
+ scaling(CpuScaling(num_cpus)),
+ cycles_per_second(GetCPUCyclesPerSecond(scaling)),
+ caches(GetCacheSizes()),
+ load_avg(GetLoadAvg()) {}
+
+const SystemInfo& SystemInfo::Get() {
+ static const SystemInfo* info = new SystemInfo();
+ return *info;
+}
+
+SystemInfo::SystemInfo() : name(GetSystemName()) {}
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_THREAD_MANAGER_H
+#define BENCHMARK_THREAD_MANAGER_H
+
+#include <atomic>
+
+#include "benchmark/benchmark.h"
+#include "mutex.h"
+
+namespace benchmark {
+namespace internal {
+
+class ThreadManager {
+ public:
+ explicit ThreadManager(int num_threads)
+ : alive_threads_(num_threads), start_stop_barrier_(num_threads) {}
+
+ Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) {
+ return benchmark_mutex_;
+ }
+
+ bool StartStopBarrier() EXCLUDES(end_cond_mutex_) {
+ return start_stop_barrier_.wait();
+ }
+
+ void NotifyThreadComplete() EXCLUDES(end_cond_mutex_) {
+ start_stop_barrier_.removeThread();
+ if (--alive_threads_ == 0) {
+ MutexLock lock(end_cond_mutex_);
+ end_condition_.notify_all();
+ }
+ }
+
+ void WaitForAllThreads() EXCLUDES(end_cond_mutex_) {
+ MutexLock lock(end_cond_mutex_);
+ end_condition_.wait(lock.native_handle(),
+ [this]() { return alive_threads_ == 0; });
+ }
+
+ public:
+ struct Result {
+ IterationCount iterations = 0;
+ double real_time_used = 0;
+ double cpu_time_used = 0;
+ double manual_time_used = 0;
+ int64_t complexity_n = 0;
+ std::string report_label_;
+ std::string error_message_;
+ bool has_error_ = false;
+ UserCounters counters;
+ };
+ GUARDED_BY(GetBenchmarkMutex()) Result results;
+
+ private:
+ mutable Mutex benchmark_mutex_;
+ std::atomic<int> alive_threads_;
+ Barrier start_stop_barrier_;
+ Mutex end_cond_mutex_;
+ Condition end_condition_;
+};
+
+} // namespace internal
+} // namespace benchmark
+
+#endif // BENCHMARK_THREAD_MANAGER_H
--- /dev/null
+#ifndef BENCHMARK_THREAD_TIMER_H
+#define BENCHMARK_THREAD_TIMER_H
+
+#include "check.h"
+#include "timers.h"
+
+namespace benchmark {
+namespace internal {
+
+class ThreadTimer {
+ explicit ThreadTimer(bool measure_process_cpu_time_)
+ : measure_process_cpu_time(measure_process_cpu_time_) {}
+
+ public:
+ static ThreadTimer Create() {
+ return ThreadTimer(/*measure_process_cpu_time_=*/false);
+ }
+ static ThreadTimer CreateProcessCpuTime() {
+ return ThreadTimer(/*measure_process_cpu_time_=*/true);
+ }
+
+ // Called by each thread
+ void StartTimer() {
+ running_ = true;
+ start_real_time_ = ChronoClockNow();
+ start_cpu_time_ = ReadCpuTimerOfChoice();
+ }
+
+ // Called by each thread
+ void StopTimer() {
+ CHECK(running_);
+ running_ = false;
+ real_time_used_ += ChronoClockNow() - start_real_time_;
+ // Floating point error can result in the subtraction producing a negative
+ // time. Guard against that.
+ cpu_time_used_ +=
+ std::max<double>(ReadCpuTimerOfChoice() - start_cpu_time_, 0);
+ }
+
+ // Called by each thread
+ void SetIterationTime(double seconds) { manual_time_used_ += seconds; }
+
+ bool running() const { return running_; }
+
+ // REQUIRES: timer is not running
+ double real_time_used() const {
+ CHECK(!running_);
+ return real_time_used_;
+ }
+
+ // REQUIRES: timer is not running
+ double cpu_time_used() const {
+ CHECK(!running_);
+ return cpu_time_used_;
+ }
+
+ // REQUIRES: timer is not running
+ double manual_time_used() const {
+ CHECK(!running_);
+ return manual_time_used_;
+ }
+
+ private:
+ double ReadCpuTimerOfChoice() const {
+ if (measure_process_cpu_time) return ProcessCPUUsage();
+ return ThreadCPUUsage();
+ }
+
+ // should the thread, or the process, time be measured?
+ const bool measure_process_cpu_time;
+
+ bool running_ = false; // Is the timer running
+ double start_real_time_ = 0; // If running_
+ double start_cpu_time_ = 0; // If running_
+
+ // Accumulated time so far (does not contain current slice if running_)
+ double real_time_used_ = 0;
+ double cpu_time_used_ = 0;
+ // Manually set iteration time. User sets this with SetIterationTime(seconds).
+ double manual_time_used_ = 0;
+};
+
+} // namespace internal
+} // namespace benchmark
+
+#endif // BENCHMARK_THREAD_TIMER_H
--- /dev/null
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "timers.h"
+#include "internal_macros.h"
+
+#ifdef BENCHMARK_OS_WINDOWS
+#include <shlwapi.h>
+#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
+#include <versionhelpers.h>
+#include <windows.h>
+#else
+#include <fcntl.h>
+#ifndef BENCHMARK_OS_FUCHSIA
+#include <sys/resource.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
+#include <unistd.h>
+#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_DRAGONFLY || \
+ defined BENCHMARK_OS_MACOSX
+#include <sys/sysctl.h>
+#endif
+#if defined(BENCHMARK_OS_MACOSX)
+#include <mach/mach_init.h>
+#include <mach/mach_port.h>
+#include <mach/thread_act.h>
+#endif
+#endif
+
+#ifdef BENCHMARK_OS_EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+#include <cerrno>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <limits>
+#include <mutex>
+
+#include "check.h"
+#include "log.h"
+#include "sleep.h"
+#include "string_util.h"
+
+namespace benchmark {
+
+// Suppress unused warnings on helper functions.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+namespace {
+#if defined(BENCHMARK_OS_WINDOWS)
+double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) {
+ ULARGE_INTEGER kernel;
+ ULARGE_INTEGER user;
+ kernel.HighPart = kernel_time.dwHighDateTime;
+ kernel.LowPart = kernel_time.dwLowDateTime;
+ user.HighPart = user_time.dwHighDateTime;
+ user.LowPart = user_time.dwLowDateTime;
+ return (static_cast<double>(kernel.QuadPart) +
+ static_cast<double>(user.QuadPart)) *
+ 1e-7;
+}
+#elif !defined(BENCHMARK_OS_FUCHSIA)
+double MakeTime(struct rusage const& ru) {
+ return (static_cast<double>(ru.ru_utime.tv_sec) +
+ static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +
+ static_cast<double>(ru.ru_stime.tv_sec) +
+ static_cast<double>(ru.ru_stime.tv_usec) * 1e-6);
+}
+#endif
+#if defined(BENCHMARK_OS_MACOSX)
+double MakeTime(thread_basic_info_data_t const& info) {
+ return (static_cast<double>(info.user_time.seconds) +
+ static_cast<double>(info.user_time.microseconds) * 1e-6 +
+ static_cast<double>(info.system_time.seconds) +
+ static_cast<double>(info.system_time.microseconds) * 1e-6);
+}
+#endif
+#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID)
+double MakeTime(struct timespec const& ts) {
+ return ts.tv_sec + (static_cast<double>(ts.tv_nsec) * 1e-9);
+}
+#endif
+
+BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {
+ std::cerr << "ERROR: " << msg << std::endl;
+ std::exit(EXIT_FAILURE);
+}
+
+} // end namespace
+
+double ProcessCPUUsage() {
+#if defined(BENCHMARK_OS_WINDOWS)
+ HANDLE proc = GetCurrentProcess();
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+ if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time,
+ &user_time))
+ return MakeTime(kernel_time, user_time);
+ DiagnoseAndExit("GetProccessTimes() failed");
+#elif defined(BENCHMARK_OS_EMSCRIPTEN)
+ // clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) returns 0 on Emscripten.
+ // Use Emscripten-specific API. Reported CPU time would be exactly the
+ // same as total time, but this is ok because there aren't long-latency
+ // syncronous system calls in Emscripten.
+ return emscripten_get_now() * 1e-3;
+#elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
+ // FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See
+ // https://github.com/google/benchmark/pull/292
+ struct timespec spec;
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0)
+ return MakeTime(spec);
+ DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");
+#else
+ struct rusage ru;
+ if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru);
+ DiagnoseAndExit("getrusage(RUSAGE_SELF, ...) failed");
+#endif
+}
+
+double ThreadCPUUsage() {
+#if defined(BENCHMARK_OS_WINDOWS)
+ HANDLE this_thread = GetCurrentThread();
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+ GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time,
+ &user_time);
+ return MakeTime(kernel_time, user_time);
+#elif defined(BENCHMARK_OS_MACOSX)
+ // FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See
+ // https://github.com/google/benchmark/pull/292
+ mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+ thread_basic_info_data_t info;
+ mach_port_t thread = pthread_mach_thread_np(pthread_self());
+ if (thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count) ==
+ KERN_SUCCESS) {
+ return MakeTime(info);
+ }
+ DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info");
+#elif defined(BENCHMARK_OS_EMSCRIPTEN)
+ // Emscripten doesn't support traditional threads
+ return ProcessCPUUsage();
+#elif defined(BENCHMARK_OS_RTEMS)
+ // RTEMS doesn't support CLOCK_THREAD_CPUTIME_ID. See
+ // https://github.com/RTEMS/rtems/blob/master/cpukit/posix/src/clockgettime.c
+ return ProcessCPUUsage();
+#elif defined(BENCHMARK_OS_SOLARIS)
+ struct rusage ru;
+ if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru);
+ DiagnoseAndExit("getrusage(RUSAGE_LWP, ...) failed");
+#elif defined(CLOCK_THREAD_CPUTIME_ID)
+ struct timespec ts;
+ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts);
+ DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");
+#else
+#error Per-thread timing is not available on your system.
+#endif
+}
+
+std::string LocalDateTimeString() {
+ // Write the local time in RFC3339 format yyyy-mm-ddTHH:MM:SS+/-HH:MM.
+ typedef std::chrono::system_clock Clock;
+ std::time_t now = Clock::to_time_t(Clock::now());
+ const std::size_t kTzOffsetLen = 6;
+ const std::size_t kTimestampLen = 19;
+
+ std::size_t tz_len;
+ std::size_t timestamp_len;
+ long int offset_minutes;
+ char tz_offset_sign = '+';
+ // tz_offset is set in one of three ways:
+ // * strftime with %z - This either returns empty or the ISO 8601 time. The maximum length an
+ // ISO 8601 string can be is 7 (e.g. -03:30, plus trailing zero).
+ // * snprintf with %c%02li:%02li - The maximum length is 41 (one for %c, up to 19 for %02li,
+ // one for :, up to 19 %02li, plus trailing zero).
+ // * A fixed string of "-00:00". The maximum length is 7 (-00:00, plus trailing zero).
+ //
+ // Thus, the maximum size this needs to be is 41.
+ char tz_offset[41];
+ // Long enough buffer to avoid format-overflow warnings
+ char storage[128];
+
+#if defined(BENCHMARK_OS_WINDOWS)
+ std::tm *timeinfo_p = ::localtime(&now);
+#else
+ std::tm timeinfo;
+ std::tm *timeinfo_p = &timeinfo;
+ ::localtime_r(&now, &timeinfo);
+#endif
+
+ tz_len = std::strftime(tz_offset, sizeof(tz_offset), "%z", timeinfo_p);
+
+ if (tz_len < kTzOffsetLen && tz_len > 1) {
+ // Timezone offset was written. strftime writes offset as +HHMM or -HHMM,
+ // RFC3339 specifies an offset as +HH:MM or -HH:MM. To convert, we parse
+ // the offset as an integer, then reprint it to a string.
+
+ offset_minutes = ::strtol(tz_offset, NULL, 10);
+ if (offset_minutes < 0) {
+ offset_minutes *= -1;
+ tz_offset_sign = '-';
+ }
+
+ tz_len = ::snprintf(tz_offset, sizeof(tz_offset), "%c%02li:%02li",
+ tz_offset_sign, offset_minutes / 100, offset_minutes % 100);
+ CHECK(tz_len == kTzOffsetLen);
+ ((void)tz_len); // Prevent unused variable warning in optimized build.
+ } else {
+ // Unknown offset. RFC3339 specifies that unknown local offsets should be
+ // written as UTC time with -00:00 timezone.
+#if defined(BENCHMARK_OS_WINDOWS)
+ // Potential race condition if another thread calls localtime or gmtime.
+ timeinfo_p = ::gmtime(&now);
+#else
+ ::gmtime_r(&now, &timeinfo);
+#endif
+
+ strncpy(tz_offset, "-00:00", kTzOffsetLen + 1);
+ }
+
+ timestamp_len = std::strftime(storage, sizeof(storage), "%Y-%m-%dT%H:%M:%S",
+ timeinfo_p);
+ CHECK(timestamp_len == kTimestampLen);
+ // Prevent unused variable warning in optimized build.
+ ((void)kTimestampLen);
+
+ std::strncat(storage, tz_offset, sizeof(storage) - timestamp_len - 1);
+ return std::string(storage);
+}
+
+} // end namespace benchmark
--- /dev/null
+#ifndef BENCHMARK_TIMERS_H
+#define BENCHMARK_TIMERS_H
+
+#include <chrono>
+#include <string>
+
+namespace benchmark {
+
+// Return the CPU usage of the current process
+double ProcessCPUUsage();
+
+// Return the CPU usage of the children of the current process
+double ChildrenCPUUsage();
+
+// Return the CPU usage of the current thread
+double ThreadCPUUsage();
+
+#if defined(HAVE_STEADY_CLOCK)
+template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
+struct ChooseSteadyClock {
+ typedef std::chrono::high_resolution_clock type;
+};
+
+template <>
+struct ChooseSteadyClock<false> {
+ typedef std::chrono::steady_clock type;
+};
+#endif
+
+struct ChooseClockType {
+#if defined(HAVE_STEADY_CLOCK)
+ typedef ChooseSteadyClock<>::type type;
+#else
+ typedef std::chrono::high_resolution_clock type;
+#endif
+};
+
+inline double ChronoClockNow() {
+ typedef ChooseClockType::type ClockType;
+ using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
+ return FpSeconds(ClockType::now().time_since_epoch()).count();
+}
+
+std::string LocalDateTimeString();
+
+} // end namespace benchmark
+
+#endif // BENCHMARK_TIMERS_H
--- /dev/null
+
+include(split_list)
+
+set(ASM_TEST_FLAGS "")
+check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
+if (BENCHMARK_HAS_O3_FLAG)
+ list(APPEND ASM_TEST_FLAGS -O3)
+endif()
+
+check_cxx_compiler_flag(-g0 BENCHMARK_HAS_G0_FLAG)
+if (BENCHMARK_HAS_G0_FLAG)
+ list(APPEND ASM_TEST_FLAGS -g0)
+endif()
+
+check_cxx_compiler_flag(-fno-stack-protector BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
+if (BENCHMARK_HAS_FNO_STACK_PROTECTOR_FLAG)
+ list(APPEND ASM_TEST_FLAGS -fno-stack-protector)
+endif()
+
+split_list(ASM_TEST_FLAGS)
+string(TOUPPER "${CMAKE_CXX_COMPILER_ID}" ASM_TEST_COMPILER)
+
+macro(add_filecheck_test name)
+ cmake_parse_arguments(ARG "" "" "CHECK_PREFIXES" ${ARGV})
+ add_library(${name} OBJECT ${name}.cc)
+ set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-S ${ASM_TEST_FLAGS}")
+ set(ASM_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${name}.s")
+ add_custom_target(copy_${name} ALL
+ COMMAND ${PROJECT_SOURCE_DIR}/tools/strip_asm.py
+ $<TARGET_OBJECTS:${name}>
+ ${ASM_OUTPUT_FILE}
+ BYPRODUCTS ${ASM_OUTPUT_FILE})
+ add_dependencies(copy_${name} ${name})
+ if (NOT ARG_CHECK_PREFIXES)
+ set(ARG_CHECK_PREFIXES "CHECK")
+ endif()
+ foreach(prefix ${ARG_CHECK_PREFIXES})
+ add_test(NAME run_${name}_${prefix}
+ COMMAND
+ ${LLVM_FILECHECK_EXE} ${name}.cc
+ --input-file=${ASM_OUTPUT_FILE}
+ --check-prefixes=CHECK,CHECK-${ASM_TEST_COMPILER}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+ endforeach()
+endmacro()
+
--- /dev/null
+TEST_COPTS = [
+ "-pedantic",
+ "-pedantic-errors",
+ "-std=c++11",
+ "-Wall",
+ "-Wextra",
+ "-Wshadow",
+ # "-Wshorten-64-to-32",
+ "-Wfloat-equal",
+ "-fstrict-aliasing",
+]
+
+PER_SRC_COPTS = ({
+ "cxx03_test.cc": ["-std=c++03"],
+ # Some of the issues with DoNotOptimize only occur when optimization is enabled
+ "donotoptimize_test.cc": ["-O3"],
+})
+
+TEST_ARGS = ["--benchmark_min_time=0.01"]
+
+PER_SRC_TEST_ARGS = ({
+ "user_counters_tabular_test.cc": ["--benchmark_counters_tabular=true"],
+ "repetitions_test.cc": [" --benchmark_repetitions=3"],
+})
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+
+cc_library(
+ name = "output_test_helper",
+ testonly = 1,
+ srcs = ["output_test_helper.cc"],
+ hdrs = ["output_test.h"],
+ copts = TEST_COPTS,
+ deps = [
+ "//:benchmark",
+ "//:benchmark_internal_headers",
+ ],
+)
+
+[
+ cc_test(
+ name = test_src[:-len(".cc")],
+ size = "small",
+ srcs = [test_src],
+ args = TEST_ARGS + PER_SRC_TEST_ARGS.get(test_src, []),
+ copts = TEST_COPTS + PER_SRC_COPTS.get(test_src, []),
+ deps = [
+ ":output_test_helper",
+ "//:benchmark",
+ "//:benchmark_internal_headers",
+ "@com_google_googletest//:gtest",
+ ] + (
+ ["@com_google_googletest//:gtest_main"] if (test_src[-len("gtest.cc"):] == "gtest.cc") else []
+ ),
+ # FIXME: Add support for assembly tests to bazel.
+ # See Issue #556
+ # https://github.com/google/benchmark/issues/556
+ )
+ for test_src in glob(
+ ["*test.cc"],
+ exclude = [
+ "*_assembly_test.cc",
+ "link_main_test.cc",
+ ],
+ )
+]
+
+cc_test(
+ name = "link_main_test",
+ size = "small",
+ srcs = ["link_main_test.cc"],
+ copts = TEST_COPTS,
+ deps = ["//:benchmark_main"],
+)
--- /dev/null
+# Enable the tests
+
+find_package(Threads REQUIRED)
+include(CheckCXXCompilerFlag)
+
+# NOTE: Some tests use `<cassert>` to perform the test. Therefore we must
+# strip -DNDEBUG from the default CMake flags in DEBUG mode.
+string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
+if( NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" )
+ add_definitions( -UNDEBUG )
+ add_definitions(-DTEST_BENCHMARK_LIBRARY_HAS_NO_ASSERTIONS)
+ # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines.
+ foreach (flags_var_to_scrub
+ CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_RELWITHDEBINFO
+ CMAKE_CXX_FLAGS_MINSIZEREL
+ CMAKE_C_FLAGS_RELEASE
+ CMAKE_C_FLAGS_RELWITHDEBINFO
+ CMAKE_C_FLAGS_MINSIZEREL)
+ string (REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " "
+ "${flags_var_to_scrub}" "${${flags_var_to_scrub}}")
+ endforeach()
+endif()
+
+check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
+set(BENCHMARK_O3_FLAG "")
+if (BENCHMARK_HAS_O3_FLAG)
+ set(BENCHMARK_O3_FLAG "-O3")
+endif()
+
+# NOTE: These flags must be added after find_package(Threads REQUIRED) otherwise
+# they will break the configuration check.
+if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
+ list(APPEND CMAKE_EXE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
+endif()
+
+add_library(output_test_helper STATIC output_test_helper.cc output_test.h)
+
+macro(compile_benchmark_test name)
+ add_executable(${name} "${name}.cc")
+ target_link_libraries(${name} benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT})
+endmacro(compile_benchmark_test)
+
+macro(compile_benchmark_test_with_main name)
+ add_executable(${name} "${name}.cc")
+ target_link_libraries(${name} benchmark::benchmark_main)
+endmacro(compile_benchmark_test_with_main)
+
+macro(compile_output_test name)
+ add_executable(${name} "${name}.cc" output_test.h)
+ target_link_libraries(${name} output_test_helper benchmark::benchmark
+ ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+endmacro(compile_output_test)
+
+# Demonstration executable
+compile_benchmark_test(benchmark_test)
+add_test(NAME benchmark COMMAND benchmark_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(filter_test)
+macro(add_filter_test name filter expect)
+ add_test(NAME ${name} COMMAND filter_test --benchmark_min_time=0.01 --benchmark_filter=${filter} ${expect})
+ add_test(NAME ${name}_list_only COMMAND filter_test --benchmark_list_tests --benchmark_filter=${filter} ${expect})
+endmacro(add_filter_test)
+
+add_filter_test(filter_simple "Foo" 3)
+add_filter_test(filter_simple_negative "-Foo" 2)
+add_filter_test(filter_suffix "BM_.*" 4)
+add_filter_test(filter_suffix_negative "-BM_.*" 1)
+add_filter_test(filter_regex_all ".*" 5)
+add_filter_test(filter_regex_all_negative "-.*" 0)
+add_filter_test(filter_regex_blank "" 5)
+add_filter_test(filter_regex_blank_negative "-" 0)
+add_filter_test(filter_regex_none "monkey" 0)
+add_filter_test(filter_regex_none_negative "-monkey" 5)
+add_filter_test(filter_regex_wildcard ".*Foo.*" 3)
+add_filter_test(filter_regex_wildcard_negative "-.*Foo.*" 2)
+add_filter_test(filter_regex_begin "^BM_.*" 4)
+add_filter_test(filter_regex_begin_negative "-^BM_.*" 1)
+add_filter_test(filter_regex_begin2 "^N" 1)
+add_filter_test(filter_regex_begin2_negative "-^N" 4)
+add_filter_test(filter_regex_end ".*Ba$" 1)
+add_filter_test(filter_regex_end_negative "-.*Ba$" 4)
+
+compile_benchmark_test(options_test)
+add_test(NAME options_benchmarks COMMAND options_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(basic_test)
+add_test(NAME basic_benchmark COMMAND basic_test --benchmark_min_time=0.01)
+
+compile_output_test(repetitions_test)
+add_test(NAME repetitions_benchmark COMMAND repetitions_test --benchmark_min_time=0.01 --benchmark_repetitions=3)
+
+compile_benchmark_test(diagnostics_test)
+add_test(NAME diagnostics_test COMMAND diagnostics_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(skip_with_error_test)
+add_test(NAME skip_with_error_test COMMAND skip_with_error_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(donotoptimize_test)
+# Some of the issues with DoNotOptimize only occur when optimization is enabled
+check_cxx_compiler_flag(-O3 BENCHMARK_HAS_O3_FLAG)
+if (BENCHMARK_HAS_O3_FLAG)
+ set_target_properties(donotoptimize_test PROPERTIES COMPILE_FLAGS "-O3")
+endif()
+add_test(NAME donotoptimize_test COMMAND donotoptimize_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(fixture_test)
+add_test(NAME fixture_test COMMAND fixture_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(register_benchmark_test)
+add_test(NAME register_benchmark_test COMMAND register_benchmark_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(map_test)
+add_test(NAME map_test COMMAND map_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(multiple_ranges_test)
+add_test(NAME multiple_ranges_test COMMAND multiple_ranges_test --benchmark_min_time=0.01)
+
+compile_benchmark_test(args_product_test)
+add_test(NAME args_product_test COMMAND args_product_test --benchmark_min_time=0.01)
+
+compile_benchmark_test_with_main(link_main_test)
+add_test(NAME link_main_test COMMAND link_main_test --benchmark_min_time=0.01)
+
+compile_output_test(reporter_output_test)
+add_test(NAME reporter_output_test COMMAND reporter_output_test --benchmark_min_time=0.01)
+
+compile_output_test(templated_fixture_test)
+add_test(NAME templated_fixture_test COMMAND templated_fixture_test --benchmark_min_time=0.01)
+
+compile_output_test(user_counters_test)
+add_test(NAME user_counters_test COMMAND user_counters_test --benchmark_min_time=0.01)
+
+compile_output_test(perf_counters_test)
+add_test(NAME perf_counters_test COMMAND perf_counters_test --benchmark_min_time=0.01 --benchmark_perf_counters=CYCLES,BRANCHES)
+
+compile_output_test(internal_threading_test)
+add_test(NAME internal_threading_test COMMAND internal_threading_test --benchmark_min_time=0.01)
+
+compile_output_test(report_aggregates_only_test)
+add_test(NAME report_aggregates_only_test COMMAND report_aggregates_only_test --benchmark_min_time=0.01)
+
+compile_output_test(display_aggregates_only_test)
+add_test(NAME display_aggregates_only_test COMMAND display_aggregates_only_test --benchmark_min_time=0.01)
+
+compile_output_test(user_counters_tabular_test)
+add_test(NAME user_counters_tabular_test COMMAND user_counters_tabular_test --benchmark_counters_tabular=true --benchmark_min_time=0.01)
+
+compile_output_test(user_counters_thousands_test)
+add_test(NAME user_counters_thousands_test COMMAND user_counters_thousands_test --benchmark_min_time=0.01)
+
+compile_output_test(memory_manager_test)
+add_test(NAME memory_manager_test COMMAND memory_manager_test --benchmark_min_time=0.01)
+
+check_cxx_compiler_flag(-std=c++03 BENCHMARK_HAS_CXX03_FLAG)
+if (BENCHMARK_HAS_CXX03_FLAG)
+ compile_benchmark_test(cxx03_test)
+ set_target_properties(cxx03_test
+ PROPERTIES
+ CXX_STANDARD 98
+ CXX_STANDARD_REQUIRED YES)
+ # libstdc++ provides different definitions within <map> between dialects. When
+ # LTO is enabled and -Werror is specified GCC diagnoses this ODR violation
+ # causing the test to fail to compile. To prevent this we explicitly disable
+ # the warning.
+ check_cxx_compiler_flag(-Wno-odr BENCHMARK_HAS_WNO_ODR)
+ if (BENCHMARK_ENABLE_LTO AND BENCHMARK_HAS_WNO_ODR)
+ set_target_properties(cxx03_test
+ PROPERTIES
+ LINK_FLAGS "-Wno-odr")
+ endif()
+ add_test(NAME cxx03 COMMAND cxx03_test --benchmark_min_time=0.01)
+endif()
+
+# Attempt to work around flaky test failures when running on Appveyor servers.
+if (DEFINED ENV{APPVEYOR})
+ set(COMPLEXITY_MIN_TIME "0.5")
+else()
+ set(COMPLEXITY_MIN_TIME "0.01")
+endif()
+compile_output_test(complexity_test)
+add_test(NAME complexity_benchmark COMMAND complexity_test --benchmark_min_time=${COMPLEXITY_MIN_TIME})
+
+###############################################################################
+# GoogleTest Unit Tests
+###############################################################################
+
+if (BENCHMARK_ENABLE_GTEST_TESTS)
+ macro(compile_gtest name)
+ add_executable(${name} "${name}.cc")
+ target_link_libraries(${name} benchmark::benchmark
+ gmock_main ${CMAKE_THREAD_LIBS_INIT})
+ endmacro(compile_gtest)
+
+ macro(add_gtest name)
+ compile_gtest(${name})
+ add_test(NAME ${name} COMMAND ${name})
+ endmacro()
+
+ add_gtest(benchmark_gtest)
+ add_gtest(benchmark_name_gtest)
+ add_gtest(benchmark_random_interleaving_gtest)
+ add_gtest(commandlineflags_gtest)
+ add_gtest(statistics_gtest)
+ add_gtest(string_util_gtest)
+ add_gtest(perf_counters_gtest)
+endif(BENCHMARK_ENABLE_GTEST_TESTS)
+
+###############################################################################
+# Assembly Unit Tests
+###############################################################################
+
+if (BENCHMARK_ENABLE_ASSEMBLY_TESTS)
+ if (NOT LLVM_FILECHECK_EXE)
+ message(FATAL_ERROR "LLVM FileCheck is required when including this file")
+ endif()
+ include(AssemblyTests.cmake)
+ add_filecheck_test(donotoptimize_assembly_test)
+ add_filecheck_test(state_assembly_test)
+ add_filecheck_test(clobber_memory_assembly_test)
+endif()
+
+
+
+###############################################################################
+# Code Coverage Configuration
+###############################################################################
+
+# Add the coverage command(s)
+if(CMAKE_BUILD_TYPE)
+ string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
+endif()
+if (${CMAKE_BUILD_TYPE_LOWER} MATCHES "coverage")
+ find_program(GCOV gcov)
+ find_program(LCOV lcov)
+ find_program(GENHTML genhtml)
+ find_program(CTEST ctest)
+ if (GCOV AND LCOV AND GENHTML AND CTEST AND HAVE_CXX_FLAG_COVERAGE)
+ add_custom_command(
+ OUTPUT ${CMAKE_BINARY_DIR}/lcov/index.html
+ COMMAND ${LCOV} -q -z -d .
+ COMMAND ${LCOV} -q --no-external -c -b "${CMAKE_SOURCE_DIR}" -d . -o before.lcov -i
+ COMMAND ${CTEST} --force-new-ctest-process
+ COMMAND ${LCOV} -q --no-external -c -b "${CMAKE_SOURCE_DIR}" -d . -o after.lcov
+ COMMAND ${LCOV} -q -a before.lcov -a after.lcov --output-file final.lcov
+ COMMAND ${LCOV} -q -r final.lcov "'${CMAKE_SOURCE_DIR}/test/*'" -o final.lcov
+ COMMAND ${GENHTML} final.lcov -o lcov --demangle-cpp --sort -p "${CMAKE_BINARY_DIR}" -t benchmark
+ DEPENDS filter_test benchmark_test options_test basic_test fixture_test cxx03_test complexity_test
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMENT "Running LCOV"
+ )
+ add_custom_target(coverage
+ DEPENDS ${CMAKE_BINARY_DIR}/lcov/index.html
+ COMMENT "LCOV report at lcov/index.html"
+ )
+ message(STATUS "Coverage command added")
+ else()
+ if (HAVE_CXX_FLAG_COVERAGE)
+ set(CXX_FLAG_COVERAGE_MESSAGE supported)
+ else()
+ set(CXX_FLAG_COVERAGE_MESSAGE unavailable)
+ endif()
+ message(WARNING
+ "Coverage not available:\n"
+ " gcov: ${GCOV}\n"
+ " lcov: ${LCOV}\n"
+ " genhtml: ${GENHTML}\n"
+ " ctest: ${CTEST}\n"
+ " --coverage flag: ${CXX_FLAG_COVERAGE_MESSAGE}")
+ endif()
+endif()
--- /dev/null
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+#include <iostream>
+#include <set>
+#include <vector>
+
+class ArgsProductFixture : public ::benchmark::Fixture {
+ public:
+ ArgsProductFixture()
+ : expectedValues({{0, 100, 2000, 30000},
+ {1, 15, 3, 8},
+ {1, 15, 3, 9},
+ {1, 15, 7, 8},
+ {1, 15, 7, 9},
+ {1, 15, 10, 8},
+ {1, 15, 10, 9},
+ {2, 15, 3, 8},
+ {2, 15, 3, 9},
+ {2, 15, 7, 8},
+ {2, 15, 7, 9},
+ {2, 15, 10, 8},
+ {2, 15, 10, 9},
+ {4, 5, 6, 11}}) {}
+
+ void SetUp(const ::benchmark::State& state) BENCHMARK_OVERRIDE {
+ std::vector<int64_t> ranges = {state.range(0), state.range(1),
+ state.range(2), state.range(3)};
+
+ assert(expectedValues.find(ranges) != expectedValues.end());
+
+ actualValues.insert(ranges);
+ }
+
+ // NOTE: This is not TearDown as we want to check after _all_ runs are
+ // complete.
+ virtual ~ArgsProductFixture() {
+ if (actualValues != expectedValues) {
+ std::cout << "EXPECTED\n";
+ for (auto v : expectedValues) {
+ std::cout << "{";
+ for (int64_t iv : v) {
+ std::cout << iv << ", ";
+ }
+ std::cout << "}\n";
+ }
+ std::cout << "ACTUAL\n";
+ for (auto v : actualValues) {
+ std::cout << "{";
+ for (int64_t iv : v) {
+ std::cout << iv << ", ";
+ }
+ std::cout << "}\n";
+ }
+ }
+ }
+
+ std::set<std::vector<int64_t>> expectedValues;
+ std::set<std::vector<int64_t>> actualValues;
+};
+
+BENCHMARK_DEFINE_F(ArgsProductFixture, Empty)(benchmark::State& state) {
+ for (auto _ : state) {
+ int64_t product =
+ state.range(0) * state.range(1) * state.range(2) * state.range(3);
+ for (int64_t x = 0; x < product; x++) {
+ benchmark::DoNotOptimize(x);
+ }
+ }
+}
+
+BENCHMARK_REGISTER_F(ArgsProductFixture, Empty)
+ ->Args({0, 100, 2000, 30000})
+ ->ArgsProduct({{1, 2}, {15}, {3, 7, 10}, {8, 9}})
+ ->Args({4, 5, 6, 11});
+
+BENCHMARK_MAIN();
--- /dev/null
+
+#include "benchmark/benchmark.h"
+
+#define BASIC_BENCHMARK_TEST(x) BENCHMARK(x)->Arg(8)->Arg(512)->Arg(8192)
+
+void BM_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+}
+BENCHMARK(BM_empty);
+BENCHMARK(BM_empty)->ThreadPerCpu();
+
+void BM_spin_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ for (int x = 0; x < state.range(0); ++x) {
+ benchmark::DoNotOptimize(x);
+ }
+ }
+}
+BASIC_BENCHMARK_TEST(BM_spin_empty);
+BASIC_BENCHMARK_TEST(BM_spin_empty)->ThreadPerCpu();
+
+void BM_spin_pause_before(benchmark::State& state) {
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ for (auto _ : state) {
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ }
+}
+BASIC_BENCHMARK_TEST(BM_spin_pause_before);
+BASIC_BENCHMARK_TEST(BM_spin_pause_before)->ThreadPerCpu();
+
+void BM_spin_pause_during(benchmark::State& state) {
+ for (auto _ : state) {
+ state.PauseTiming();
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ state.ResumeTiming();
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ }
+}
+BASIC_BENCHMARK_TEST(BM_spin_pause_during);
+BASIC_BENCHMARK_TEST(BM_spin_pause_during)->ThreadPerCpu();
+
+void BM_pause_during(benchmark::State& state) {
+ for (auto _ : state) {
+ state.PauseTiming();
+ state.ResumeTiming();
+ }
+}
+BENCHMARK(BM_pause_during);
+BENCHMARK(BM_pause_during)->ThreadPerCpu();
+BENCHMARK(BM_pause_during)->UseRealTime();
+BENCHMARK(BM_pause_during)->UseRealTime()->ThreadPerCpu();
+
+void BM_spin_pause_after(benchmark::State& state) {
+ for (auto _ : state) {
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ }
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+}
+BASIC_BENCHMARK_TEST(BM_spin_pause_after);
+BASIC_BENCHMARK_TEST(BM_spin_pause_after)->ThreadPerCpu();
+
+void BM_spin_pause_before_and_after(benchmark::State& state) {
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ for (auto _ : state) {
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+ }
+ for (int i = 0; i < state.range(0); ++i) {
+ benchmark::DoNotOptimize(i);
+ }
+}
+BASIC_BENCHMARK_TEST(BM_spin_pause_before_and_after);
+BASIC_BENCHMARK_TEST(BM_spin_pause_before_and_after)->ThreadPerCpu();
+
+void BM_empty_stop_start(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_empty_stop_start);
+BENCHMARK(BM_empty_stop_start)->ThreadPerCpu();
+
+
+void BM_KeepRunning(benchmark::State& state) {
+ benchmark::IterationCount iter_count = 0;
+ assert(iter_count == state.iterations());
+ while (state.KeepRunning()) {
+ ++iter_count;
+ }
+ assert(iter_count == state.iterations());
+}
+BENCHMARK(BM_KeepRunning);
+
+void BM_KeepRunningBatch(benchmark::State& state) {
+ // Choose a batch size >1000 to skip the typical runs with iteration
+ // targets of 10, 100 and 1000. If these are not actually skipped the
+ // bug would be detectable as consecutive runs with the same iteration
+ // count. Below we assert that this does not happen.
+ const benchmark::IterationCount batch_size = 1009;
+
+ static benchmark::IterationCount prior_iter_count = 0;
+ benchmark::IterationCount iter_count = 0;
+ while (state.KeepRunningBatch(batch_size)) {
+ iter_count += batch_size;
+ }
+ assert(state.iterations() == iter_count);
+
+ // Verify that the iteration count always increases across runs (see
+ // comment above).
+ assert(iter_count == batch_size // max_iterations == 1
+ || iter_count > prior_iter_count); // max_iterations > batch_size
+ prior_iter_count = iter_count;
+}
+// Register with a fixed repetition count to establish the invariant that
+// the iteration count should always change across runs. This overrides
+// the --benchmark_repetitions command line flag, which would otherwise
+// cause this test to fail if set > 1.
+BENCHMARK(BM_KeepRunningBatch)->Repetitions(1);
+
+void BM_RangedFor(benchmark::State& state) {
+ benchmark::IterationCount iter_count = 0;
+ for (auto _ : state) {
+ ++iter_count;
+ }
+ assert(iter_count == state.max_iterations);
+}
+BENCHMARK(BM_RangedFor);
+
+// Ensure that StateIterator provides all the necessary typedefs required to
+// instantiate std::iterator_traits.
+static_assert(std::is_same<
+ typename std::iterator_traits<benchmark::State::StateIterator>::value_type,
+ typename benchmark::State::StateIterator::value_type>::value, "");
+
+BENCHMARK_MAIN();
--- /dev/null
+#include <map>
+#include <string>
+#include <vector>
+
+#include "../src/benchmark_register.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace benchmark {
+namespace internal {
+extern std::map<std::string, std::string>* global_context;
+
+namespace {
+
+TEST(AddRangeTest, Simple) {
+ std::vector<int> dst;
+ AddRange(&dst, 1, 2, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(1, 2));
+}
+
+TEST(AddRangeTest, Simple64) {
+ std::vector<int64_t> dst;
+ AddRange(&dst, static_cast<int64_t>(1), static_cast<int64_t>(2), 2);
+ EXPECT_THAT(dst, testing::ElementsAre(1, 2));
+}
+
+TEST(AddRangeTest, Advanced) {
+ std::vector<int> dst;
+ AddRange(&dst, 5, 15, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(5, 8, 15));
+}
+
+TEST(AddRangeTest, Advanced64) {
+ std::vector<int64_t> dst;
+ AddRange(&dst, static_cast<int64_t>(5), static_cast<int64_t>(15), 2);
+ EXPECT_THAT(dst, testing::ElementsAre(5, 8, 15));
+}
+
+TEST(AddRangeTest, FullRange8) {
+ std::vector<int8_t> dst;
+ AddRange(&dst, int8_t{1}, std::numeric_limits<int8_t>::max(), 8);
+ EXPECT_THAT(dst, testing::ElementsAre(1, 8, 64, 127));
+}
+
+TEST(AddRangeTest, FullRange64) {
+ std::vector<int64_t> dst;
+ AddRange(&dst, int64_t{1}, std::numeric_limits<int64_t>::max(), 1024);
+ EXPECT_THAT(
+ dst, testing::ElementsAre(1LL, 1024LL, 1048576LL, 1073741824LL,
+ 1099511627776LL, 1125899906842624LL,
+ 1152921504606846976LL, 9223372036854775807LL));
+}
+
+TEST(AddRangeTest, NegativeRanges) {
+ std::vector<int> dst;
+ AddRange(&dst, -8, 0, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(-8, -4, -2, -1, 0));
+}
+
+TEST(AddRangeTest, StrictlyNegative) {
+ std::vector<int> dst;
+ AddRange(&dst, -8, -1, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(-8, -4, -2, -1));
+}
+
+TEST(AddRangeTest, SymmetricNegativeRanges) {
+ std::vector<int> dst;
+ AddRange(&dst, -8, 8, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(-8, -4, -2, -1, 0, 1, 2, 4, 8));
+}
+
+TEST(AddRangeTest, SymmetricNegativeRangesOddMult) {
+ std::vector<int> dst;
+ AddRange(&dst, -30, 32, 5);
+ EXPECT_THAT(dst, testing::ElementsAre(-30, -25, -5, -1, 0, 1, 5, 25, 32));
+}
+
+TEST(AddRangeTest, NegativeRangesAsymmetric) {
+ std::vector<int> dst;
+ AddRange(&dst, -3, 5, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(-3, -2, -1, 0, 1, 2, 4, 5));
+}
+
+TEST(AddRangeTest, NegativeRangesLargeStep) {
+ // Always include -1, 0, 1 when crossing zero.
+ std::vector<int> dst;
+ AddRange(&dst, -8, 8, 10);
+ EXPECT_THAT(dst, testing::ElementsAre(-8, -1, 0, 1, 8));
+}
+
+TEST(AddRangeTest, ZeroOnlyRange) {
+ std::vector<int> dst;
+ AddRange(&dst, 0, 0, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(0));
+}
+
+TEST(AddRangeTest, ZeroStartingRange) {
+ std::vector<int> dst;
+ AddRange(&dst, 0, 2, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(0, 1, 2));
+}
+
+TEST(AddRangeTest, NegativeRange64) {
+ std::vector<int64_t> dst;
+ AddRange<int64_t>(&dst, -4, 4, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(-4, -2, -1, 0, 1, 2, 4));
+}
+
+TEST(AddRangeTest, NegativeRangePreservesExistingOrder) {
+ // If elements already exist in the range, ensure we don't change
+ // their ordering by adding negative values.
+ std::vector<int64_t> dst = {1, 2, 3};
+ AddRange<int64_t>(&dst, -2, 2, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(1, 2, 3, -2, -1, 0, 1, 2));
+}
+
+TEST(AddRangeTest, FullNegativeRange64) {
+ std::vector<int64_t> dst;
+ const auto min = std::numeric_limits<int64_t>::min();
+ const auto max = std::numeric_limits<int64_t>::max();
+ AddRange(&dst, min, max, 1024);
+ EXPECT_THAT(
+ dst, testing::ElementsAreArray(std::vector<int64_t>{
+ min, -1152921504606846976LL, -1125899906842624LL,
+ -1099511627776LL, -1073741824LL, -1048576LL, -1024LL, -1LL, 0LL,
+ 1LL, 1024LL, 1048576LL, 1073741824LL, 1099511627776LL,
+ 1125899906842624LL, 1152921504606846976LL, max}));
+}
+
+TEST(AddRangeTest, Simple8) {
+ std::vector<int8_t> dst;
+ AddRange<int8_t>(&dst, 1, 8, 2);
+ EXPECT_THAT(dst, testing::ElementsAre(1, 2, 4, 8));
+}
+
+TEST(AddCustomContext, Simple) {
+ EXPECT_THAT(global_context, nullptr);
+
+ AddCustomContext("foo", "bar");
+ AddCustomContext("baz", "qux");
+
+ EXPECT_THAT(*global_context,
+ testing::UnorderedElementsAre(testing::Pair("foo", "bar"),
+ testing::Pair("baz", "qux")));
+
+ delete global_context;
+ global_context = nullptr;
+}
+
+TEST(AddCustomContext, DuplicateKey) {
+ EXPECT_THAT(global_context, nullptr);
+
+ AddCustomContext("foo", "bar");
+ AddCustomContext("foo", "qux");
+
+ EXPECT_THAT(*global_context,
+ testing::UnorderedElementsAre(testing::Pair("foo", "bar")));
+
+ delete global_context;
+ global_context = nullptr;
+}
+
+} // namespace
+} // namespace internal
+} // namespace benchmark
--- /dev/null
+#include "benchmark/benchmark.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace benchmark;
+using namespace benchmark::internal;
+
+TEST(BenchmarkNameTest, Empty) {
+ const auto name = BenchmarkName();
+ EXPECT_EQ(name.str(), std::string());
+}
+
+TEST(BenchmarkNameTest, FunctionName) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ EXPECT_EQ(name.str(), "function_name");
+}
+
+TEST(BenchmarkNameTest, FunctionNameAndArgs) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.args = "some_args:3/4/5";
+ EXPECT_EQ(name.str(), "function_name/some_args:3/4/5");
+}
+
+TEST(BenchmarkNameTest, MinTime) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.args = "some_args:3/4";
+ name.min_time = "min_time:3.4s";
+ EXPECT_EQ(name.str(), "function_name/some_args:3/4/min_time:3.4s");
+}
+
+TEST(BenchmarkNameTest, Iterations) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.min_time = "min_time:3.4s";
+ name.iterations = "iterations:42";
+ EXPECT_EQ(name.str(), "function_name/min_time:3.4s/iterations:42");
+}
+
+TEST(BenchmarkNameTest, Repetitions) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.min_time = "min_time:3.4s";
+ name.repetitions = "repetitions:24";
+ EXPECT_EQ(name.str(), "function_name/min_time:3.4s/repetitions:24");
+}
+
+TEST(BenchmarkNameTest, TimeType) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.min_time = "min_time:3.4s";
+ name.time_type = "hammer_time";
+ EXPECT_EQ(name.str(), "function_name/min_time:3.4s/hammer_time");
+}
+
+TEST(BenchmarkNameTest, Threads) {
+ auto name = BenchmarkName();
+ name.function_name = "function_name";
+ name.min_time = "min_time:3.4s";
+ name.threads = "threads:256";
+ EXPECT_EQ(name.str(), "function_name/min_time:3.4s/threads:256");
+}
+
+TEST(BenchmarkNameTest, TestEmptyFunctionName) {
+ auto name = BenchmarkName();
+ name.args = "first:3/second:4";
+ name.threads = "threads:22";
+ EXPECT_EQ(name.str(), "first:3/second:4/threads:22");
+}
+
+} // end namespace
--- /dev/null
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "../src/commandlineflags.h"
+#include "../src/string_util.h"
+#include "benchmark/benchmark.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+DECLARE_bool(benchmark_enable_random_interleaving);
+DECLARE_string(benchmark_filter);
+DECLARE_int32(benchmark_repetitions);
+
+namespace benchmark {
+namespace internal {
+namespace {
+
+class EventQueue : public std::queue<std::string> {
+ public:
+ void Put(const std::string& event) { push(event); }
+
+ void Clear() {
+ while (!empty()) {
+ pop();
+ }
+ }
+
+ std::string Get() {
+ std::string event = front();
+ pop();
+ return event;
+ }
+};
+
+static EventQueue* queue = new EventQueue;
+
+class NullReporter : public BenchmarkReporter {
+ public:
+ bool ReportContext(const Context& /*context*/) override { return true; }
+ void ReportRuns(const std::vector<Run>& /* report */) override {}
+};
+
+class BenchmarkTest : public testing::Test {
+ public:
+ static void SetupHook(int /* num_threads */) { queue->push("Setup"); }
+
+ static void TeardownHook(int /* num_threads */) { queue->push("Teardown"); }
+
+ void Execute(const std::string& pattern) {
+ queue->Clear();
+
+ BenchmarkReporter* reporter = new NullReporter;
+ FLAGS_benchmark_filter = pattern;
+ RunSpecifiedBenchmarks(reporter);
+ delete reporter;
+
+ queue->Put("DONE"); // End marker
+ }
+};
+
+static void BM_Match1(benchmark::State& state) {
+ const int64_t arg = state.range(0);
+
+ for (auto _ : state) {
+ }
+ queue->Put(StrFormat("BM_Match1/%d", static_cast<int>(arg)));
+}
+BENCHMARK(BM_Match1)
+ ->Iterations(100)
+ ->Arg(1)
+ ->Arg(2)
+ ->Arg(3)
+ ->Range(10, 80)
+ ->Args({90})
+ ->Args({100});
+
+TEST_F(BenchmarkTest, Match1) {
+ Execute("BM_Match1");
+ ASSERT_EQ("BM_Match1/1", queue->Get());
+ ASSERT_EQ("BM_Match1/2", queue->Get());
+ ASSERT_EQ("BM_Match1/3", queue->Get());
+ ASSERT_EQ("BM_Match1/10", queue->Get());
+ ASSERT_EQ("BM_Match1/64", queue->Get());
+ ASSERT_EQ("BM_Match1/80", queue->Get());
+ ASSERT_EQ("BM_Match1/90", queue->Get());
+ ASSERT_EQ("BM_Match1/100", queue->Get());
+ ASSERT_EQ("DONE", queue->Get());
+}
+
+TEST_F(BenchmarkTest, Match1WithRepetition) {
+ FLAGS_benchmark_repetitions = 2;
+
+ Execute("BM_Match1/(64|80)");
+ ASSERT_EQ("BM_Match1/64", queue->Get());
+ ASSERT_EQ("BM_Match1/64", queue->Get());
+ ASSERT_EQ("BM_Match1/80", queue->Get());
+ ASSERT_EQ("BM_Match1/80", queue->Get());
+ ASSERT_EQ("DONE", queue->Get());
+}
+
+TEST_F(BenchmarkTest, Match1WithRandomInterleaving) {
+ FLAGS_benchmark_enable_random_interleaving = true;
+ FLAGS_benchmark_repetitions = 100;
+
+ std::map<std::string, int> element_count;
+ std::map<std::string, int> interleaving_count;
+ Execute("BM_Match1/(64|80)");
+ for (int i = 0; i < 100; ++i) {
+ std::vector<std::string> interleaving;
+ interleaving.push_back(queue->Get());
+ interleaving.push_back(queue->Get());
+ element_count[interleaving[0].c_str()]++;
+ element_count[interleaving[1].c_str()]++;
+ interleaving_count[StrFormat("%s,%s", interleaving[0].c_str(),
+ interleaving[1].c_str())]++;
+ }
+ EXPECT_EQ(element_count["BM_Match1/64"], 100) << "Unexpected repetitions.";
+ EXPECT_EQ(element_count["BM_Match1/80"], 100) << "Unexpected repetitions.";
+ EXPECT_GE(interleaving_count.size(), 2) << "Interleaving was not randomized.";
+ ASSERT_EQ("DONE", queue->Get());
+}
+
+} // namespace
+} // namespace internal
+} // namespace benchmark
--- /dev/null
+#include "benchmark/benchmark.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <list>
+#include <map>
+#include <mutex>
+#include <set>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#if defined(__GNUC__)
+#define BENCHMARK_NOINLINE __attribute__((noinline))
+#else
+#define BENCHMARK_NOINLINE
+#endif
+
+namespace {
+
+int BENCHMARK_NOINLINE Factorial(uint32_t n) {
+ return (n == 1) ? 1 : n * Factorial(n - 1);
+}
+
+double CalculatePi(int depth) {
+ double pi = 0.0;
+ for (int i = 0; i < depth; ++i) {
+ double numerator = static_cast<double>(((i % 2) * 2) - 1);
+ double denominator = static_cast<double>((2 * i) - 1);
+ pi += numerator / denominator;
+ }
+ return (pi - 1.0) * 4;
+}
+
+std::set<int64_t> ConstructRandomSet(int64_t size) {
+ std::set<int64_t> s;
+ for (int i = 0; i < size; ++i) s.insert(s.end(), i);
+ return s;
+}
+
+std::mutex test_vector_mu;
+std::vector<int>* test_vector = nullptr;
+
+} // end namespace
+
+static void BM_Factorial(benchmark::State& state) {
+ int fac_42 = 0;
+ for (auto _ : state) fac_42 = Factorial(8);
+ // Prevent compiler optimizations
+ std::stringstream ss;
+ ss << fac_42;
+ state.SetLabel(ss.str());
+}
+BENCHMARK(BM_Factorial);
+BENCHMARK(BM_Factorial)->UseRealTime();
+
+static void BM_CalculatePiRange(benchmark::State& state) {
+ double pi = 0.0;
+ for (auto _ : state) pi = CalculatePi(static_cast<int>(state.range(0)));
+ std::stringstream ss;
+ ss << pi;
+ state.SetLabel(ss.str());
+}
+BENCHMARK_RANGE(BM_CalculatePiRange, 1, 1024 * 1024);
+
+static void BM_CalculatePi(benchmark::State& state) {
+ static const int depth = 1024;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(CalculatePi(static_cast<int>(depth)));
+ }
+}
+BENCHMARK(BM_CalculatePi)->Threads(8);
+BENCHMARK(BM_CalculatePi)->ThreadRange(1, 32);
+BENCHMARK(BM_CalculatePi)->ThreadPerCpu();
+
+static void BM_SetInsert(benchmark::State& state) {
+ std::set<int64_t> data;
+ for (auto _ : state) {
+ state.PauseTiming();
+ data = ConstructRandomSet(state.range(0));
+ state.ResumeTiming();
+ for (int j = 0; j < state.range(1); ++j) data.insert(rand());
+ }
+ state.SetItemsProcessed(state.iterations() * state.range(1));
+ state.SetBytesProcessed(state.iterations() * state.range(1) * sizeof(int));
+}
+
+// Test many inserts at once to reduce the total iterations needed. Otherwise, the slower,
+// non-timed part of each iteration will make the benchmark take forever.
+BENCHMARK(BM_SetInsert)->Ranges({{1 << 10, 8 << 10}, {128, 512}});
+
+template <typename Container,
+ typename ValueType = typename Container::value_type>
+static void BM_Sequential(benchmark::State& state) {
+ ValueType v = 42;
+ for (auto _ : state) {
+ Container c;
+ for (int64_t i = state.range(0); --i;) c.push_back(v);
+ }
+ const int64_t items_processed = state.iterations() * state.range(0);
+ state.SetItemsProcessed(items_processed);
+ state.SetBytesProcessed(items_processed * sizeof(v));
+}
+BENCHMARK_TEMPLATE2(BM_Sequential, std::vector<int>, int)
+ ->Range(1 << 0, 1 << 10);
+BENCHMARK_TEMPLATE(BM_Sequential, std::list<int>)->Range(1 << 0, 1 << 10);
+// Test the variadic version of BENCHMARK_TEMPLATE in C++11 and beyond.
+#ifdef BENCHMARK_HAS_CXX11
+BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>, int)->Arg(512);
+#endif
+
+static void BM_StringCompare(benchmark::State& state) {
+ size_t len = static_cast<size_t>(state.range(0));
+ std::string s1(len, '-');
+ std::string s2(len, '-');
+ for (auto _ : state) benchmark::DoNotOptimize(s1.compare(s2));
+}
+BENCHMARK(BM_StringCompare)->Range(1, 1 << 20);
+
+static void BM_SetupTeardown(benchmark::State& state) {
+ if (state.thread_index == 0) {
+ // No need to lock test_vector_mu here as this is running single-threaded.
+ test_vector = new std::vector<int>();
+ }
+ int i = 0;
+ for (auto _ : state) {
+ std::lock_guard<std::mutex> l(test_vector_mu);
+ if (i % 2 == 0)
+ test_vector->push_back(i);
+ else
+ test_vector->pop_back();
+ ++i;
+ }
+ if (state.thread_index == 0) {
+ delete test_vector;
+ }
+}
+BENCHMARK(BM_SetupTeardown)->ThreadPerCpu();
+
+static void BM_LongTest(benchmark::State& state) {
+ double tracker = 0.0;
+ for (auto _ : state) {
+ for (int i = 0; i < state.range(0); ++i)
+ benchmark::DoNotOptimize(tracker += i);
+ }
+}
+BENCHMARK(BM_LongTest)->Range(1 << 16, 1 << 28);
+
+static void BM_ParallelMemset(benchmark::State& state) {
+ int64_t size = state.range(0) / static_cast<int64_t>(sizeof(int));
+ int thread_size = static_cast<int>(size) / state.threads;
+ int from = thread_size * state.thread_index;
+ int to = from + thread_size;
+
+ if (state.thread_index == 0) {
+ test_vector = new std::vector<int>(static_cast<size_t>(size));
+ }
+
+ for (auto _ : state) {
+ for (int i = from; i < to; i++) {
+ // No need to lock test_vector_mu as ranges
+ // do not overlap between threads.
+ benchmark::DoNotOptimize(test_vector->at(i) = 1);
+ }
+ }
+
+ if (state.thread_index == 0) {
+ delete test_vector;
+ }
+}
+BENCHMARK(BM_ParallelMemset)->Arg(10 << 20)->ThreadRange(1, 4);
+
+static void BM_ManualTiming(benchmark::State& state) {
+ int64_t slept_for = 0;
+ int64_t microseconds = state.range(0);
+ std::chrono::duration<double, std::micro> sleep_duration{
+ static_cast<double>(microseconds)};
+
+ for (auto _ : state) {
+ auto start = std::chrono::high_resolution_clock::now();
+ // Simulate some useful workload with a sleep
+ std::this_thread::sleep_for(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration));
+ auto end = std::chrono::high_resolution_clock::now();
+
+ auto elapsed =
+ std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
+
+ state.SetIterationTime(elapsed.count());
+ slept_for += microseconds;
+ }
+ state.SetItemsProcessed(slept_for);
+}
+BENCHMARK(BM_ManualTiming)->Range(1, 1 << 14)->UseRealTime();
+BENCHMARK(BM_ManualTiming)->Range(1, 1 << 14)->UseManualTime();
+
+#ifdef BENCHMARK_HAS_CXX11
+
+template <class... Args>
+void BM_with_args(benchmark::State& state, Args&&...) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK_CAPTURE(BM_with_args, int_test, 42, 43, 44);
+BENCHMARK_CAPTURE(BM_with_args, string_and_pair_test, std::string("abc"),
+ std::pair<int, double>(42, 3.8));
+
+void BM_non_template_args(benchmark::State& state, int, double) {
+ while(state.KeepRunning()) {}
+}
+BENCHMARK_CAPTURE(BM_non_template_args, basic_test, 0, 0);
+
+#endif // BENCHMARK_HAS_CXX11
+
+static void BM_DenseThreadRanges(benchmark::State& st) {
+ switch (st.range(0)) {
+ case 1:
+ assert(st.threads == 1 || st.threads == 2 || st.threads == 3);
+ break;
+ case 2:
+ assert(st.threads == 1 || st.threads == 3 || st.threads == 4);
+ break;
+ case 3:
+ assert(st.threads == 5 || st.threads == 8 || st.threads == 11 ||
+ st.threads == 14);
+ break;
+ default:
+ assert(false && "Invalid test case number");
+ }
+ while (st.KeepRunning()) {
+ }
+}
+BENCHMARK(BM_DenseThreadRanges)->Arg(1)->DenseThreadRange(1, 3);
+BENCHMARK(BM_DenseThreadRanges)->Arg(2)->DenseThreadRange(1, 4, 2);
+BENCHMARK(BM_DenseThreadRanges)->Arg(3)->DenseThreadRange(5, 14, 3);
+
+BENCHMARK_MAIN();
--- /dev/null
+#include <benchmark/benchmark.h>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wreturn-type"
+#endif
+
+extern "C" {
+
+extern int ExternInt;
+extern int ExternInt2;
+extern int ExternInt3;
+
+}
+
+// CHECK-LABEL: test_basic:
+extern "C" void test_basic() {
+ int x;
+ benchmark::DoNotOptimize(&x);
+ x = 101;
+ benchmark::ClobberMemory();
+ // CHECK: leaq [[DEST:[^,]+]], %rax
+ // CHECK: movl $101, [[DEST]]
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_redundant_store:
+extern "C" void test_redundant_store() {
+ ExternInt = 3;
+ benchmark::ClobberMemory();
+ ExternInt = 51;
+ // CHECK-DAG: ExternInt
+ // CHECK-DAG: movl $3
+ // CHECK: movl $51
+}
+
+// CHECK-LABEL: test_redundant_read:
+extern "C" void test_redundant_read() {
+ int x;
+ benchmark::DoNotOptimize(&x);
+ x = ExternInt;
+ benchmark::ClobberMemory();
+ x = ExternInt2;
+ // CHECK: leaq [[DEST:[^,]+]], %rax
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, [[DEST]]
+ // CHECK-NOT: ExternInt2
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_redundant_read2:
+extern "C" void test_redundant_read2() {
+ int x;
+ benchmark::DoNotOptimize(&x);
+ x = ExternInt;
+ benchmark::ClobberMemory();
+ x = ExternInt2;
+ benchmark::ClobberMemory();
+ // CHECK: leaq [[DEST:[^,]+]], %rax
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, [[DEST]]
+ // CHECK: ExternInt2(%rip)
+ // CHECK: movl %eax, [[DEST]]
+ // CHECK: ret
+}
--- /dev/null
+#include <cstdlib>
+
+#include "../src/commandlineflags.h"
+#include "../src/internal_macros.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace benchmark {
+namespace {
+
+#if defined(BENCHMARK_OS_WINDOWS)
+int setenv(const char* name, const char* value, int overwrite) {
+ if (!overwrite) {
+ // NOTE: getenv_s is far superior but not available under mingw.
+ char* env_value = getenv(name);
+ if (env_value == nullptr) {
+ return -1;
+ }
+ }
+ return _putenv_s(name, value);
+}
+
+int unsetenv(const char* name) { return _putenv_s(name, ""); }
+
+#endif // BENCHMARK_OS_WINDOWS
+
+TEST(BoolFromEnv, Default) {
+ ASSERT_EQ(unsetenv("NOT_IN_ENV"), 0);
+ EXPECT_EQ(BoolFromEnv("not_in_env", true), true);
+}
+
+TEST(BoolFromEnv, False) {
+ ASSERT_EQ(setenv("IN_ENV", "0", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "N", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "n", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "NO", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "No", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "no", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "F", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "f", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "FALSE", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "False", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "false", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "OFF", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "Off", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "off", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", true), false);
+ unsetenv("IN_ENV");
+}
+
+TEST(BoolFromEnv, True) {
+ ASSERT_EQ(setenv("IN_ENV", "1", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "Y", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "y", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "YES", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "Yes", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "yes", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "T", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "t", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "TRUE", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "True", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "true", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "ON", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "On", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+ ASSERT_EQ(setenv("IN_ENV", "on", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+
+#ifndef BENCHMARK_OS_WINDOWS
+ ASSERT_EQ(setenv("IN_ENV", "", 1), 0);
+ EXPECT_EQ(BoolFromEnv("in_env", false), true);
+ unsetenv("IN_ENV");
+#endif
+}
+
+TEST(Int32FromEnv, NotInEnv) {
+ ASSERT_EQ(unsetenv("NOT_IN_ENV"), 0);
+ EXPECT_EQ(Int32FromEnv("not_in_env", 42), 42);
+}
+
+TEST(Int32FromEnv, InvalidInteger) {
+ ASSERT_EQ(setenv("IN_ENV", "foo", 1), 0);
+ EXPECT_EQ(Int32FromEnv("in_env", 42), 42);
+ unsetenv("IN_ENV");
+}
+
+TEST(Int32FromEnv, ValidInteger) {
+ ASSERT_EQ(setenv("IN_ENV", "42", 1), 0);
+ EXPECT_EQ(Int32FromEnv("in_env", 64), 42);
+ unsetenv("IN_ENV");
+}
+
+TEST(DoubleFromEnv, NotInEnv) {
+ ASSERT_EQ(unsetenv("NOT_IN_ENV"), 0);
+ EXPECT_EQ(DoubleFromEnv("not_in_env", 0.51), 0.51);
+}
+
+TEST(DoubleFromEnv, InvalidReal) {
+ ASSERT_EQ(setenv("IN_ENV", "foo", 1), 0);
+ EXPECT_EQ(DoubleFromEnv("in_env", 0.51), 0.51);
+ unsetenv("IN_ENV");
+}
+
+TEST(DoubleFromEnv, ValidReal) {
+ ASSERT_EQ(setenv("IN_ENV", "0.51", 1), 0);
+ EXPECT_EQ(DoubleFromEnv("in_env", 0.71), 0.51);
+ unsetenv("IN_ENV");
+}
+
+TEST(StringFromEnv, Default) {
+ ASSERT_EQ(unsetenv("NOT_IN_ENV"), 0);
+ EXPECT_STREQ(StringFromEnv("not_in_env", "foo"), "foo");
+}
+
+TEST(StringFromEnv, Valid) {
+ ASSERT_EQ(setenv("IN_ENV", "foo", 1), 0);
+ EXPECT_STREQ(StringFromEnv("in_env", "bar"), "foo");
+ unsetenv("IN_ENV");
+}
+
+TEST(KvPairsFromEnv, Default) {
+ ASSERT_EQ(unsetenv("NOT_IN_ENV"), 0);
+ EXPECT_THAT(KvPairsFromEnv("not_in_env", {{"foo", "bar"}}),
+ testing::ElementsAre(testing::Pair("foo", "bar")));
+}
+
+TEST(KvPairsFromEnv, MalformedReturnsDefault) {
+ ASSERT_EQ(setenv("IN_ENV", "foo", 1), 0);
+ EXPECT_THAT(KvPairsFromEnv("in_env", {{"foo", "bar"}}),
+ testing::ElementsAre(testing::Pair("foo", "bar")));
+ unsetenv("IN_ENV");
+}
+
+TEST(KvPairsFromEnv, Single) {
+ ASSERT_EQ(setenv("IN_ENV", "foo=bar", 1), 0);
+ EXPECT_THAT(KvPairsFromEnv("in_env", {}),
+ testing::ElementsAre(testing::Pair("foo", "bar")));
+ unsetenv("IN_ENV");
+}
+
+TEST(KvPairsFromEnv, Multiple) {
+ ASSERT_EQ(setenv("IN_ENV", "foo=bar,baz=qux", 1), 0);
+ EXPECT_THAT(KvPairsFromEnv("in_env", {}),
+ testing::UnorderedElementsAre(testing::Pair("foo", "bar"),
+ testing::Pair("baz", "qux")));
+ unsetenv("IN_ENV");
+}
+
+} // namespace
+} // namespace benchmark
--- /dev/null
+#undef NDEBUG
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <vector>
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+namespace {
+
+#define ADD_COMPLEXITY_CASES(...) \
+ int CONCAT(dummy, __LINE__) = AddComplexityTest(__VA_ARGS__)
+
+int AddComplexityTest(std::string test_name, std::string big_o_test_name,
+ std::string rms_test_name, std::string big_o,
+ int family_index) {
+ SetSubstitutions({{"%name", test_name},
+ {"%bigo_name", big_o_test_name},
+ {"%rms_name", rms_test_name},
+ {"%bigo_str", "[ ]* %float " + big_o},
+ {"%bigo", big_o},
+ {"%rms", "[ ]*[0-9]+ %"}});
+ AddCases(
+ TC_ConsoleOut,
+ {{"^%bigo_name %bigo_str %bigo_str[ ]*$"},
+ {"^%bigo_name", MR_Not}, // Assert we we didn't only matched a name.
+ {"^%rms_name %rms %rms[ ]*$", MR_Next}});
+ AddCases(
+ TC_JSONOut,
+ {{"\"name\": \"%bigo_name\",$"},
+ {"\"family_index\": " + std::to_string(family_index) + ",$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"%name\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": %int,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"BigO\",$", MR_Next},
+ {"\"cpu_coefficient\": %float,$", MR_Next},
+ {"\"real_coefficient\": %float,$", MR_Next},
+ {"\"big_o\": \"%bigo\",$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next},
+ {"\"name\": \"%rms_name\",$"},
+ {"\"family_index\": " + std::to_string(family_index) + ",$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"%name\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": %int,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"RMS\",$", MR_Next},
+ {"\"rms\": %float$", MR_Next},
+ {"}", MR_Next}});
+ AddCases(TC_CSVOut, {{"^\"%bigo_name\",,%float,%float,%bigo,,,,,$"},
+ {"^\"%bigo_name\"", MR_Not},
+ {"^\"%rms_name\",,%float,%float,,,,,,$", MR_Next}});
+ return 0;
+}
+
+} // end namespace
+
+// ========================================================================= //
+// --------------------------- Testing BigO O(1) --------------------------- //
+// ========================================================================= //
+
+void BM_Complexity_O1(benchmark::State& state) {
+ for (auto _ : state) {
+ for (int i = 0; i < 1024; ++i) {
+ benchmark::DoNotOptimize(&i);
+ }
+ }
+ state.SetComplexityN(state.range(0));
+}
+BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity(benchmark::o1);
+BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity();
+BENCHMARK(BM_Complexity_O1)
+ ->Range(1, 1 << 18)
+ ->Complexity([](benchmark::IterationCount) { return 1.0; });
+
+const char *one_test_name = "BM_Complexity_O1";
+const char *big_o_1_test_name = "BM_Complexity_O1_BigO";
+const char *rms_o_1_test_name = "BM_Complexity_O1_RMS";
+const char *enum_big_o_1 = "\\([0-9]+\\)";
+// FIXME: Tolerate both '(1)' and 'lgN' as output when the complexity is auto
+// deduced.
+// See https://github.com/google/benchmark/issues/272
+const char *auto_big_o_1 = "(\\([0-9]+\\))|(lgN)";
+const char *lambda_big_o_1 = "f\\(N\\)";
+
+// Add enum tests
+ADD_COMPLEXITY_CASES(one_test_name, big_o_1_test_name, rms_o_1_test_name,
+ enum_big_o_1, /*family_index=*/0);
+
+// Add auto enum tests
+ADD_COMPLEXITY_CASES(one_test_name, big_o_1_test_name, rms_o_1_test_name,
+ auto_big_o_1, /*family_index=*/1);
+
+// Add lambda tests
+ADD_COMPLEXITY_CASES(one_test_name, big_o_1_test_name, rms_o_1_test_name,
+ lambda_big_o_1, /*family_index=*/2);
+
+// ========================================================================= //
+// --------------------------- Testing BigO O(N) --------------------------- //
+// ========================================================================= //
+
+std::vector<int> ConstructRandomVector(int64_t size) {
+ std::vector<int> v;
+ v.reserve(static_cast<int>(size));
+ for (int i = 0; i < size; ++i) {
+ v.push_back(static_cast<int>(std::rand() % size));
+ }
+ return v;
+}
+
+void BM_Complexity_O_N(benchmark::State& state) {
+ auto v = ConstructRandomVector(state.range(0));
+ // Test worst case scenario (item not in vector)
+ const int64_t item_not_in_vector = state.range(0) * 2;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(std::find(v.begin(), v.end(), item_not_in_vector));
+ }
+ state.SetComplexityN(state.range(0));
+}
+BENCHMARK(BM_Complexity_O_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity(benchmark::oN);
+BENCHMARK(BM_Complexity_O_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity([](benchmark::IterationCount n) -> double {
+ return static_cast<double>(n);
+ });
+BENCHMARK(BM_Complexity_O_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity();
+
+const char *n_test_name = "BM_Complexity_O_N";
+const char *big_o_n_test_name = "BM_Complexity_O_N_BigO";
+const char *rms_o_n_test_name = "BM_Complexity_O_N_RMS";
+const char *enum_auto_big_o_n = "N";
+const char *lambda_big_o_n = "f\\(N\\)";
+
+// Add enum tests
+ADD_COMPLEXITY_CASES(n_test_name, big_o_n_test_name, rms_o_n_test_name,
+ enum_auto_big_o_n, /*family_index=*/3);
+
+// Add lambda tests
+ADD_COMPLEXITY_CASES(n_test_name, big_o_n_test_name, rms_o_n_test_name,
+ lambda_big_o_n, /*family_index=*/4);
+
+// ========================================================================= //
+// ------------------------- Testing BigO O(N*lgN) ------------------------- //
+// ========================================================================= //
+
+static void BM_Complexity_O_N_log_N(benchmark::State& state) {
+ auto v = ConstructRandomVector(state.range(0));
+ for (auto _ : state) {
+ std::sort(v.begin(), v.end());
+ }
+ state.SetComplexityN(state.range(0));
+}
+static const double kLog2E = 1.44269504088896340736;
+BENCHMARK(BM_Complexity_O_N_log_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity(benchmark::oNLogN);
+BENCHMARK(BM_Complexity_O_N_log_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity([](benchmark::IterationCount n) {
+ return kLog2E * n * log(static_cast<double>(n));
+ });
+BENCHMARK(BM_Complexity_O_N_log_N)
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 16)
+ ->Complexity();
+
+const char *n_lg_n_test_name = "BM_Complexity_O_N_log_N";
+const char *big_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_BigO";
+const char *rms_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_RMS";
+const char *enum_auto_big_o_n_lg_n = "NlgN";
+const char *lambda_big_o_n_lg_n = "f\\(N\\)";
+
+// Add enum tests
+ADD_COMPLEXITY_CASES(n_lg_n_test_name, big_o_n_lg_n_test_name,
+ rms_o_n_lg_n_test_name, enum_auto_big_o_n_lg_n,
+ /*family_index=*/6);
+
+// Add lambda tests
+ADD_COMPLEXITY_CASES(n_lg_n_test_name, big_o_n_lg_n_test_name,
+ rms_o_n_lg_n_test_name, lambda_big_o_n_lg_n,
+ /*family_index=*/7);
+
+// ========================================================================= //
+// -------- Testing formatting of Complexity with captured args ------------ //
+// ========================================================================= //
+
+void BM_ComplexityCaptureArgs(benchmark::State& state, int n) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ state.SetComplexityN(n);
+}
+
+BENCHMARK_CAPTURE(BM_ComplexityCaptureArgs, capture_test, 100)
+ ->Complexity(benchmark::oN)
+ ->Ranges({{1, 2}, {3, 4}});
+
+const std::string complexity_capture_name =
+ "BM_ComplexityCaptureArgs/capture_test";
+
+ADD_COMPLEXITY_CASES(complexity_capture_name, complexity_capture_name + "_BigO",
+ complexity_capture_name + "_RMS", "N", /*family_index=*/9);
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char *argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+#undef NDEBUG
+#include <cassert>
+#include <cstddef>
+
+#include "benchmark/benchmark.h"
+
+#if __cplusplus >= 201103L
+#error C++11 or greater detected. Should be C++03.
+#endif
+
+#ifdef BENCHMARK_HAS_CXX11
+#error C++11 or greater detected by the library. BENCHMARK_HAS_CXX11 is defined.
+#endif
+
+void BM_empty(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ volatile benchmark::IterationCount x = state.iterations();
+ ((void)x);
+ }
+}
+BENCHMARK(BM_empty);
+
+// The new C++11 interface for args/ranges requires initializer list support.
+// Therefore we provide the old interface to support C++03.
+void BM_old_arg_range_interface(benchmark::State& state) {
+ assert((state.range(0) == 1 && state.range(1) == 2) ||
+ (state.range(0) == 5 && state.range(1) == 6));
+ while (state.KeepRunning()) {
+ }
+}
+BENCHMARK(BM_old_arg_range_interface)->ArgPair(1, 2)->RangePair(5, 5, 6, 6);
+
+template <class T, class U>
+void BM_template2(benchmark::State& state) {
+ BM_empty(state);
+}
+BENCHMARK_TEMPLATE2(BM_template2, int, long);
+
+template <class T>
+void BM_template1(benchmark::State& state) {
+ BM_empty(state);
+}
+BENCHMARK_TEMPLATE(BM_template1, long);
+BENCHMARK_TEMPLATE1(BM_template1, int);
+
+template <class T>
+struct BM_Fixture : public ::benchmark::Fixture {
+};
+
+BENCHMARK_TEMPLATE_F(BM_Fixture, BM_template1, long)(benchmark::State& state) {
+ BM_empty(state);
+}
+BENCHMARK_TEMPLATE1_F(BM_Fixture, BM_template2, int)(benchmark::State& state) {
+ BM_empty(state);
+}
+
+void BM_counters(benchmark::State& state) {
+ BM_empty(state);
+ state.counters["Foo"] = 2;
+}
+BENCHMARK(BM_counters);
+
+BENCHMARK_MAIN();
--- /dev/null
+// Testing:
+// State::PauseTiming()
+// State::ResumeTiming()
+// Test that CHECK's within these function diagnose when they are called
+// outside of the KeepRunning() loop.
+//
+// NOTE: Users should NOT include or use src/check.h. This is only done in
+// order to test library internals.
+
+#include <cstdlib>
+#include <stdexcept>
+
+#include "../src/check.h"
+#include "benchmark/benchmark.h"
+
+#if defined(__GNUC__) && !defined(__EXCEPTIONS)
+#define TEST_HAS_NO_EXCEPTIONS
+#endif
+
+void TestHandler() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ throw std::logic_error("");
+#else
+ std::abort();
+#endif
+}
+
+void try_invalid_pause_resume(benchmark::State& state) {
+#if !defined(TEST_BENCHMARK_LIBRARY_HAS_NO_ASSERTIONS) && !defined(TEST_HAS_NO_EXCEPTIONS)
+ try {
+ state.PauseTiming();
+ std::abort();
+ } catch (std::logic_error const&) {
+ }
+ try {
+ state.ResumeTiming();
+ std::abort();
+ } catch (std::logic_error const&) {
+ }
+#else
+ (void)state; // avoid unused warning
+#endif
+}
+
+void BM_diagnostic_test(benchmark::State& state) {
+ static bool called_once = false;
+
+ if (called_once == false) try_invalid_pause_resume(state);
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+
+ if (called_once == false) try_invalid_pause_resume(state);
+
+ called_once = true;
+}
+BENCHMARK(BM_diagnostic_test);
+
+
+void BM_diagnostic_test_keep_running(benchmark::State& state) {
+ static bool called_once = false;
+
+ if (called_once == false) try_invalid_pause_resume(state);
+
+ while(state.KeepRunning()) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+
+ if (called_once == false) try_invalid_pause_resume(state);
+
+ called_once = true;
+}
+BENCHMARK(BM_diagnostic_test_keep_running);
+
+int main(int argc, char* argv[]) {
+ benchmark::internal::GetAbortHandler() = &TestHandler;
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+}
--- /dev/null
+
+#undef NDEBUG
+#include <cstdio>
+#include <string>
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// Ok this test is super ugly. We want to check what happens with the file
+// reporter in the presence of DisplayAggregatesOnly().
+// We do not care about console output, the normal tests check that already.
+
+void BM_SummaryRepeat(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_SummaryRepeat)->Repetitions(3)->DisplayAggregatesOnly();
+
+int main(int argc, char* argv[]) {
+ const std::string output = GetFileReporterOutput(argc, argv);
+
+ if (SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3") != 6 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3\"") != 3 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_mean\"") != 1 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_median\"") !=
+ 1 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_stddev\"") !=
+ 1) {
+ std::cout << "Precondition mismatch. Expected to only find 6 "
+ "occurrences of \"BM_SummaryRepeat/repeats:3\" substring:\n"
+ "\"name\": \"BM_SummaryRepeat/repeats:3\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3_mean\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3_median\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3_stddev\"\nThe entire "
+ "output:\n";
+ std::cout << output;
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+#include <benchmark/benchmark.h>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wreturn-type"
+#endif
+
+extern "C" {
+
+extern int ExternInt;
+extern int ExternInt2;
+extern int ExternInt3;
+
+inline int Add42(int x) { return x + 42; }
+
+struct NotTriviallyCopyable {
+ NotTriviallyCopyable();
+ explicit NotTriviallyCopyable(int x) : value(x) {}
+ NotTriviallyCopyable(NotTriviallyCopyable const&);
+ int value;
+};
+
+struct Large {
+ int value;
+ int data[2];
+};
+
+}
+// CHECK-LABEL: test_with_rvalue:
+extern "C" void test_with_rvalue() {
+ benchmark::DoNotOptimize(Add42(0));
+ // CHECK: movl $42, %eax
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_large_rvalue:
+extern "C" void test_with_large_rvalue() {
+ benchmark::DoNotOptimize(Large{ExternInt, {ExternInt, ExternInt}});
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG:[a-z]+]]
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_non_trivial_rvalue:
+extern "C" void test_with_non_trivial_rvalue() {
+ benchmark::DoNotOptimize(NotTriviallyCopyable(ExternInt));
+ // CHECK: mov{{l|q}} ExternInt(%rip)
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_lvalue:
+extern "C" void test_with_lvalue() {
+ int x = 101;
+ benchmark::DoNotOptimize(x);
+ // CHECK-GNU: movl $101, %eax
+ // CHECK-CLANG: movl $101, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_large_lvalue:
+extern "C" void test_with_large_lvalue() {
+ Large L{ExternInt, {ExternInt, ExternInt}};
+ benchmark::DoNotOptimize(L);
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_non_trivial_lvalue:
+extern "C" void test_with_non_trivial_lvalue() {
+ NotTriviallyCopyable NTC(ExternInt);
+ benchmark::DoNotOptimize(NTC);
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_const_lvalue:
+extern "C" void test_with_const_lvalue() {
+ const int x = 123;
+ benchmark::DoNotOptimize(x);
+ // CHECK: movl $123, %eax
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_large_const_lvalue:
+extern "C" void test_with_large_const_lvalue() {
+ const Large L{ExternInt, {ExternInt, ExternInt}};
+ benchmark::DoNotOptimize(L);
+ // CHECK: ExternInt(%rip)
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: movl %eax, -{{[0-9]+}}(%[[REG]])
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_with_non_trivial_const_lvalue:
+extern "C" void test_with_non_trivial_const_lvalue() {
+ const NotTriviallyCopyable Obj(ExternInt);
+ benchmark::DoNotOptimize(Obj);
+ // CHECK: mov{{q|l}} ExternInt(%rip)
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_div_by_two:
+extern "C" int test_div_by_two(int input) {
+ int divisor = 2;
+ benchmark::DoNotOptimize(divisor);
+ return input / divisor;
+ // CHECK: movl $2, [[DEST:.*]]
+ // CHECK: idivl [[DEST]]
+ // CHECK: ret
+}
+
+// CHECK-LABEL: test_inc_integer:
+extern "C" int test_inc_integer() {
+ int x = 0;
+ for (int i=0; i < 5; ++i)
+ benchmark::DoNotOptimize(++x);
+ // CHECK: movl $1, [[DEST:.*]]
+ // CHECK: {{(addl \$1,|incl)}} [[DEST]]
+ // CHECK: {{(addl \$1,|incl)}} [[DEST]]
+ // CHECK: {{(addl \$1,|incl)}} [[DEST]]
+ // CHECK: {{(addl \$1,|incl)}} [[DEST]]
+ // CHECK-CLANG: movl [[DEST]], %eax
+ // CHECK: ret
+ return x;
+}
+
+// CHECK-LABEL: test_pointer_rvalue
+extern "C" void test_pointer_rvalue() {
+ // CHECK: movl $42, [[DEST:.*]]
+ // CHECK: leaq [[DEST]], %rax
+ // CHECK-CLANG: movq %rax, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: ret
+ int x = 42;
+ benchmark::DoNotOptimize(&x);
+}
+
+// CHECK-LABEL: test_pointer_const_lvalue:
+extern "C" void test_pointer_const_lvalue() {
+ // CHECK: movl $42, [[DEST:.*]]
+ // CHECK: leaq [[DEST]], %rax
+ // CHECK-CLANG: movq %rax, -{{[0-9]+}}(%[[REG:[a-z]+]])
+ // CHECK: ret
+ int x = 42;
+ int * const xp = &x;
+ benchmark::DoNotOptimize(xp);
+}
+
+// CHECK-LABEL: test_pointer_lvalue:
+extern "C" void test_pointer_lvalue() {
+ // CHECK: movl $42, [[DEST:.*]]
+ // CHECK: leaq [[DEST]], %rax
+ // CHECK-CLANG: movq %rax, -{{[0-9]+}}(%[[REG:[a-z+]+]])
+ // CHECK: ret
+ int x = 42;
+ int *xp = &x;
+ benchmark::DoNotOptimize(xp);
+}
--- /dev/null
+#include "benchmark/benchmark.h"
+
+#include <cstdint>
+
+namespace {
+#if defined(__GNUC__)
+std::uint64_t double_up(const std::uint64_t x) __attribute__((const));
+#endif
+std::uint64_t double_up(const std::uint64_t x) { return x * 2; }
+}
+
+// Using DoNotOptimize on types like BitRef seem to cause a lot of problems
+// with the inline assembly on both GCC and Clang.
+struct BitRef {
+ int index;
+ unsigned char &byte;
+
+public:
+ static BitRef Make() {
+ static unsigned char arr[2] = {};
+ BitRef b(1, arr[0]);
+ return b;
+ }
+private:
+ BitRef(int i, unsigned char& b) : index(i), byte(b) {}
+};
+
+int main(int, char*[]) {
+ // this test verifies compilation of DoNotOptimize() for some types
+
+ char buffer8[8] = "";
+ benchmark::DoNotOptimize(buffer8);
+
+ char buffer20[20] = "";
+ benchmark::DoNotOptimize(buffer20);
+
+ char buffer1024[1024] = "";
+ benchmark::DoNotOptimize(buffer1024);
+ benchmark::DoNotOptimize(&buffer1024[0]);
+
+ int x = 123;
+ benchmark::DoNotOptimize(x);
+ benchmark::DoNotOptimize(&x);
+ benchmark::DoNotOptimize(x += 42);
+
+ benchmark::DoNotOptimize(double_up(x));
+
+ // These tests are to e
+ benchmark::DoNotOptimize(BitRef::Make());
+ BitRef lval = BitRef::Make();
+ benchmark::DoNotOptimize(lval);
+}
--- /dev/null
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+class TestReporter : public benchmark::ConsoleReporter {
+ public:
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE {
+ return ConsoleReporter::ReportContext(context);
+ };
+
+ virtual void ReportRuns(const std::vector<Run>& report) BENCHMARK_OVERRIDE {
+ ++count_;
+ max_family_index_ =
+ std::max<size_t>(max_family_index_, report[0].family_index);
+ ConsoleReporter::ReportRuns(report);
+ };
+
+ TestReporter() : count_(0), max_family_index_(0) {}
+
+ virtual ~TestReporter() {}
+
+ size_t GetCount() const { return count_; }
+
+ size_t GetMaxFamilyIndex() const { return max_family_index_; }
+
+ private:
+ mutable size_t count_;
+ mutable size_t max_family_index_;
+};
+
+} // end namespace
+
+static void NoPrefix(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(NoPrefix);
+
+static void BM_Foo(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_Foo);
+
+static void BM_Bar(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_Bar);
+
+static void BM_FooBar(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_FooBar);
+
+static void BM_FooBa(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_FooBa);
+
+int main(int argc, char **argv) {
+ bool list_only = false;
+ for (int i = 0; i < argc; ++i)
+ list_only |= std::string(argv[i]).find("--benchmark_list_tests") !=
+ std::string::npos;
+
+ benchmark::Initialize(&argc, argv);
+
+ TestReporter test_reporter;
+ const size_t returned_count =
+ benchmark::RunSpecifiedBenchmarks(&test_reporter);
+
+ if (argc == 2) {
+ // Make sure we ran all of the tests
+ std::stringstream ss(argv[1]);
+ size_t expected_return;
+ ss >> expected_return;
+
+ if (returned_count != expected_return) {
+ std::cerr << "ERROR: Expected " << expected_return
+ << " tests to match the filter but returned_count = "
+ << returned_count << std::endl;
+ return -1;
+ }
+
+ const size_t expected_reports = list_only ? 0 : expected_return;
+ const size_t reports_count = test_reporter.GetCount();
+ if (reports_count != expected_reports) {
+ std::cerr << "ERROR: Expected " << expected_reports
+ << " tests to be run but reported_count = " << reports_count
+ << std::endl;
+ return -1;
+ }
+
+ const size_t max_family_index = test_reporter.GetMaxFamilyIndex();
+ const size_t num_families = reports_count == 0 ? 0 : 1 + max_family_index;
+ if (num_families != expected_reports) {
+ std::cerr << "ERROR: Expected " << expected_reports
+ << " test families to be run but num_families = "
+ << num_families << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+#include <memory>
+
+#define FIXTURE_BECHMARK_NAME MyFixture
+
+class FIXTURE_BECHMARK_NAME : public ::benchmark::Fixture {
+ public:
+ void SetUp(const ::benchmark::State& state) BENCHMARK_OVERRIDE {
+ if (state.thread_index == 0) {
+ assert(data.get() == nullptr);
+ data.reset(new int(42));
+ }
+ }
+
+ void TearDown(const ::benchmark::State& state) BENCHMARK_OVERRIDE {
+ if (state.thread_index == 0) {
+ assert(data.get() != nullptr);
+ data.reset();
+ }
+ }
+
+ ~FIXTURE_BECHMARK_NAME() { assert(data == nullptr); }
+
+ std::unique_ptr<int> data;
+};
+
+BENCHMARK_F(FIXTURE_BECHMARK_NAME, Foo)(benchmark::State &st) {
+ assert(data.get() != nullptr);
+ assert(*data == 42);
+ for (auto _ : st) {
+ }
+}
+
+BENCHMARK_DEFINE_F(FIXTURE_BECHMARK_NAME, Bar)(benchmark::State& st) {
+ if (st.thread_index == 0) {
+ assert(data.get() != nullptr);
+ assert(*data == 42);
+ }
+ for (auto _ : st) {
+ assert(data.get() != nullptr);
+ assert(*data == 42);
+ }
+ st.SetItemsProcessed(st.range(0));
+}
+BENCHMARK_REGISTER_F(FIXTURE_BECHMARK_NAME, Bar)->Arg(42);
+BENCHMARK_REGISTER_F(FIXTURE_BECHMARK_NAME, Bar)->Arg(42)->ThreadPerCpu();
+
+BENCHMARK_MAIN();
--- /dev/null
+
+#undef NDEBUG
+
+#include <chrono>
+#include <thread>
+#include "../src/timers.h"
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+static const std::chrono::duration<double, std::milli> time_frame(50);
+static const double time_frame_in_sec(
+ std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(
+ time_frame)
+ .count());
+
+void MyBusySpinwait() {
+ const auto start = benchmark::ChronoClockNow();
+
+ while (true) {
+ const auto now = benchmark::ChronoClockNow();
+ const auto elapsed = now - start;
+
+ if (std::chrono::duration<double, std::chrono::seconds::period>(elapsed) >=
+ time_frame)
+ return;
+ }
+}
+
+// ========================================================================= //
+// --------------------------- TEST CASES BEGIN ---------------------------- //
+// ========================================================================= //
+
+// ========================================================================= //
+// BM_MainThread
+
+void BM_MainThread(benchmark::State& state) {
+ for (auto _ : state) {
+ MyBusySpinwait();
+ state.SetIterationTime(time_frame_in_sec);
+ }
+ state.counters["invtime"] =
+ benchmark::Counter{1, benchmark::Counter::kIsRate};
+}
+
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(1);
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(1)->UseRealTime();
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(1)->UseManualTime();
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(1)->MeasureProcessCPUTime();
+BENCHMARK(BM_MainThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_MainThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(2);
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(2)->UseRealTime();
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(2)->UseManualTime();
+BENCHMARK(BM_MainThread)->Iterations(1)->Threads(2)->MeasureProcessCPUTime();
+BENCHMARK(BM_MainThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_MainThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+// ========================================================================= //
+// BM_WorkerThread
+
+void BM_WorkerThread(benchmark::State& state) {
+ for (auto _ : state) {
+ std::thread Worker(&MyBusySpinwait);
+ Worker.join();
+ state.SetIterationTime(time_frame_in_sec);
+ }
+ state.counters["invtime"] =
+ benchmark::Counter{1, benchmark::Counter::kIsRate};
+}
+
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(1);
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(1)->UseRealTime();
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(1)->UseManualTime();
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(1)->MeasureProcessCPUTime();
+BENCHMARK(BM_WorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_WorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(2);
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(2)->UseRealTime();
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(2)->UseManualTime();
+BENCHMARK(BM_WorkerThread)->Iterations(1)->Threads(2)->MeasureProcessCPUTime();
+BENCHMARK(BM_WorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_WorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+// ========================================================================= //
+// BM_MainThreadAndWorkerThread
+
+void BM_MainThreadAndWorkerThread(benchmark::State& state) {
+ for (auto _ : state) {
+ std::thread Worker(&MyBusySpinwait);
+ MyBusySpinwait();
+ Worker.join();
+ state.SetIterationTime(time_frame_in_sec);
+ }
+ state.counters["invtime"] =
+ benchmark::Counter{1, benchmark::Counter::kIsRate};
+}
+
+BENCHMARK(BM_MainThreadAndWorkerThread)->Iterations(1)->Threads(1);
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->UseRealTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->UseManualTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(1)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+BENCHMARK(BM_MainThreadAndWorkerThread)->Iterations(1)->Threads(2);
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->UseRealTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->UseManualTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseRealTime();
+BENCHMARK(BM_MainThreadAndWorkerThread)
+ ->Iterations(1)
+ ->Threads(2)
+ ->MeasureProcessCPUTime()
+ ->UseManualTime();
+
+// ========================================================================= //
+// ---------------------------- TEST CASES END ----------------------------- //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+#include "benchmark/benchmark.h"
+
+void BM_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+}
+BENCHMARK(BM_empty);
--- /dev/null
+#include "benchmark/benchmark.h"
+
+#include <cstdlib>
+#include <map>
+
+namespace {
+
+std::map<int, int> ConstructRandomMap(int size) {
+ std::map<int, int> m;
+ for (int i = 0; i < size; ++i) {
+ m.insert(std::make_pair(std::rand() % size, std::rand() % size));
+ }
+ return m;
+}
+
+} // namespace
+
+// Basic version.
+static void BM_MapLookup(benchmark::State& state) {
+ const int size = static_cast<int>(state.range(0));
+ std::map<int, int> m;
+ for (auto _ : state) {
+ state.PauseTiming();
+ m = ConstructRandomMap(size);
+ state.ResumeTiming();
+ for (int i = 0; i < size; ++i) {
+ benchmark::DoNotOptimize(m.find(std::rand() % size));
+ }
+ }
+ state.SetItemsProcessed(state.iterations() * size);
+}
+BENCHMARK(BM_MapLookup)->Range(1 << 3, 1 << 12);
+
+// Using fixtures.
+class MapFixture : public ::benchmark::Fixture {
+ public:
+ void SetUp(const ::benchmark::State& st) BENCHMARK_OVERRIDE {
+ m = ConstructRandomMap(static_cast<int>(st.range(0)));
+ }
+
+ void TearDown(const ::benchmark::State&) BENCHMARK_OVERRIDE { m.clear(); }
+
+ std::map<int, int> m;
+};
+
+BENCHMARK_DEFINE_F(MapFixture, Lookup)(benchmark::State& state) {
+ const int size = static_cast<int>(state.range(0));
+ for (auto _ : state) {
+ for (int i = 0; i < size; ++i) {
+ benchmark::DoNotOptimize(m.find(std::rand() % size));
+ }
+ }
+ state.SetItemsProcessed(state.iterations() * size);
+}
+BENCHMARK_REGISTER_F(MapFixture, Lookup)->Range(1 << 3, 1 << 12);
+
+BENCHMARK_MAIN();
--- /dev/null
+#include <memory>
+
+#include "../src/check.h"
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+class TestMemoryManager : public benchmark::MemoryManager {
+ void Start() BENCHMARK_OVERRIDE {}
+ void Stop(Result* result) BENCHMARK_OVERRIDE {
+ result->num_allocs = 42;
+ result->max_bytes_used = 42000;
+ }
+};
+
+void BM_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+}
+BENCHMARK(BM_empty);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_empty %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_empty\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_empty\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"allocs_per_iter\": %float,$", MR_Next},
+ {"\"max_bytes_used\": 42000$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_empty\",%csv_report$"}});
+
+int main(int argc, char* argv[]) {
+ std::unique_ptr<benchmark::MemoryManager> mm(new TestMemoryManager());
+
+ benchmark::RegisterMemoryManager(mm.get());
+ RunOutputTests(argc, argv);
+ benchmark::RegisterMemoryManager(nullptr);
+}
--- /dev/null
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+#include <iostream>
+#include <set>
+#include <vector>
+
+class MultipleRangesFixture : public ::benchmark::Fixture {
+ public:
+ MultipleRangesFixture()
+ : expectedValues({{1, 3, 5},
+ {1, 3, 8},
+ {1, 3, 15},
+ {2, 3, 5},
+ {2, 3, 8},
+ {2, 3, 15},
+ {1, 4, 5},
+ {1, 4, 8},
+ {1, 4, 15},
+ {2, 4, 5},
+ {2, 4, 8},
+ {2, 4, 15},
+ {1, 7, 5},
+ {1, 7, 8},
+ {1, 7, 15},
+ {2, 7, 5},
+ {2, 7, 8},
+ {2, 7, 15},
+ {7, 6, 3}}) {}
+
+ void SetUp(const ::benchmark::State& state) BENCHMARK_OVERRIDE {
+ std::vector<int64_t> ranges = {state.range(0), state.range(1),
+ state.range(2)};
+
+ assert(expectedValues.find(ranges) != expectedValues.end());
+
+ actualValues.insert(ranges);
+ }
+
+ // NOTE: This is not TearDown as we want to check after _all_ runs are
+ // complete.
+ virtual ~MultipleRangesFixture() {
+ if (actualValues != expectedValues) {
+ std::cout << "EXPECTED\n";
+ for (auto v : expectedValues) {
+ std::cout << "{";
+ for (int64_t iv : v) {
+ std::cout << iv << ", ";
+ }
+ std::cout << "}\n";
+ }
+ std::cout << "ACTUAL\n";
+ for (auto v : actualValues) {
+ std::cout << "{";
+ for (int64_t iv : v) {
+ std::cout << iv << ", ";
+ }
+ std::cout << "}\n";
+ }
+ }
+ }
+
+ std::set<std::vector<int64_t>> expectedValues;
+ std::set<std::vector<int64_t>> actualValues;
+};
+
+BENCHMARK_DEFINE_F(MultipleRangesFixture, Empty)(benchmark::State& state) {
+ for (auto _ : state) {
+ int64_t product = state.range(0) * state.range(1) * state.range(2);
+ for (int64_t x = 0; x < product; x++) {
+ benchmark::DoNotOptimize(x);
+ }
+ }
+}
+
+BENCHMARK_REGISTER_F(MultipleRangesFixture, Empty)
+ ->RangeMultiplier(2)
+ ->Ranges({{1, 2}, {3, 7}, {5, 15}})
+ ->Args({7, 6, 3});
+
+void BM_CheckDefaultArgument(benchmark::State& state) {
+ // Test that the 'range()' without an argument is the same as 'range(0)'.
+ assert(state.range() == state.range(0));
+ assert(state.range() != state.range(1));
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_CheckDefaultArgument)->Ranges({{1, 5}, {6, 10}});
+
+static void BM_MultipleRanges(benchmark::State& st) {
+ for (auto _ : st) {
+ }
+}
+BENCHMARK(BM_MultipleRanges)->Ranges({{5, 5}, {6, 6}});
+
+BENCHMARK_MAIN();
--- /dev/null
+#include "benchmark/benchmark.h"
+#include <chrono>
+#include <thread>
+
+#if defined(NDEBUG)
+#undef NDEBUG
+#endif
+#include <cassert>
+
+void BM_basic(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+
+void BM_basic_slow(benchmark::State& state) {
+ std::chrono::milliseconds sleep_duration(state.range(0));
+ for (auto _ : state) {
+ std::this_thread::sleep_for(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration));
+ }
+}
+
+BENCHMARK(BM_basic);
+BENCHMARK(BM_basic)->Arg(42);
+BENCHMARK(BM_basic_slow)->Arg(10)->Unit(benchmark::kNanosecond);
+BENCHMARK(BM_basic_slow)->Arg(100)->Unit(benchmark::kMicrosecond);
+BENCHMARK(BM_basic_slow)->Arg(1000)->Unit(benchmark::kMillisecond);
+BENCHMARK(BM_basic_slow)->Arg(1000)->Unit(benchmark::kSecond);
+BENCHMARK(BM_basic)->Range(1, 8);
+BENCHMARK(BM_basic)->RangeMultiplier(2)->Range(1, 8);
+BENCHMARK(BM_basic)->DenseRange(10, 15);
+BENCHMARK(BM_basic)->Args({42, 42});
+BENCHMARK(BM_basic)->Ranges({{64, 512}, {64, 512}});
+BENCHMARK(BM_basic)->MinTime(0.7);
+BENCHMARK(BM_basic)->UseRealTime();
+BENCHMARK(BM_basic)->ThreadRange(2, 4);
+BENCHMARK(BM_basic)->ThreadPerCpu();
+BENCHMARK(BM_basic)->Repetitions(3);
+BENCHMARK(BM_basic)
+ ->RangeMultiplier(std::numeric_limits<int>::max())
+ ->Range(std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max());
+
+// Negative ranges
+BENCHMARK(BM_basic)->Range(-64, -1);
+BENCHMARK(BM_basic)->RangeMultiplier(4)->Range(-8, 8);
+BENCHMARK(BM_basic)->DenseRange(-2, 2, 1);
+BENCHMARK(BM_basic)->Ranges({{-64, 1}, {-8, -1}});
+
+void CustomArgs(benchmark::internal::Benchmark* b) {
+ for (int i = 0; i < 10; ++i) {
+ b->Arg(i);
+ }
+}
+
+BENCHMARK(BM_basic)->Apply(CustomArgs);
+
+void BM_explicit_iteration_count(benchmark::State& state) {
+ // Test that benchmarks specified with an explicit iteration count are
+ // only run once.
+ static bool invoked_before = false;
+ assert(!invoked_before);
+ invoked_before = true;
+
+ // Test that the requested iteration count is respected.
+ assert(state.max_iterations == 42);
+ size_t actual_iterations = 0;
+ for (auto _ : state)
+ ++actual_iterations;
+ assert(state.iterations() == state.max_iterations);
+ assert(state.iterations() == 42);
+
+}
+BENCHMARK(BM_explicit_iteration_count)->Iterations(42);
+
+BENCHMARK_MAIN();
--- /dev/null
+#ifndef TEST_OUTPUT_TEST_H
+#define TEST_OUTPUT_TEST_H
+
+#undef NDEBUG
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "../src/re.h"
+#include "benchmark/benchmark.h"
+
+#define CONCAT2(x, y) x##y
+#define CONCAT(x, y) CONCAT2(x, y)
+
+#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = ::AddCases(__VA_ARGS__)
+
+#define SET_SUBSTITUTIONS(...) \
+ int CONCAT(dummy, __LINE__) = ::SetSubstitutions(__VA_ARGS__)
+
+enum MatchRules {
+ MR_Default, // Skip non-matching lines until a match is found.
+ MR_Next, // Match must occur on the next line.
+ MR_Not // No line between the current position and the next match matches
+ // the regex
+};
+
+struct TestCase {
+ TestCase(std::string re, int rule = MR_Default);
+
+ std::string regex_str;
+ int match_rule;
+ std::string substituted_regex;
+ std::shared_ptr<benchmark::Regex> regex;
+};
+
+enum TestCaseID {
+ TC_ConsoleOut,
+ TC_ConsoleErr,
+ TC_JSONOut,
+ TC_JSONErr,
+ TC_CSVOut,
+ TC_CSVErr,
+
+ TC_NumID // PRIVATE
+};
+
+// Add a list of test cases to be run against the output specified by
+// 'ID'
+int AddCases(TestCaseID ID, std::initializer_list<TestCase> il);
+
+// Add or set a list of substitutions to be performed on constructed regex's
+// See 'output_test_helper.cc' for a list of default substitutions.
+int SetSubstitutions(
+ std::initializer_list<std::pair<std::string, std::string>> il);
+
+// Run all output tests.
+void RunOutputTests(int argc, char* argv[]);
+
+// Count the number of 'pat' substrings in the 'haystack' string.
+int SubstrCnt(const std::string& haystack, const std::string& pat);
+
+// Run registered benchmarks with file reporter enabled, and return the content
+// outputted by the file reporter.
+std::string GetFileReporterOutput(int argc, char* argv[]);
+
+// ========================================================================= //
+// ------------------------- Results checking ------------------------------ //
+// ========================================================================= //
+
+// Call this macro to register a benchmark for checking its results. This
+// should be all that's needed. It subscribes a function to check the (CSV)
+// results of a benchmark. This is done only after verifying that the output
+// strings are really as expected.
+// bm_name_pattern: a name or a regex pattern which will be matched against
+// all the benchmark names. Matching benchmarks
+// will be the subject of a call to checker_function
+// checker_function: should be of type ResultsCheckFn (see below)
+#define CHECK_BENCHMARK_RESULTS(bm_name_pattern, checker_function) \
+ size_t CONCAT(dummy, __LINE__) = AddChecker(bm_name_pattern, checker_function)
+
+struct Results;
+typedef std::function<void(Results const&)> ResultsCheckFn;
+
+size_t AddChecker(const char* bm_name_pattern, ResultsCheckFn fn);
+
+// Class holding the results of a benchmark.
+// It is passed in calls to checker functions.
+struct Results {
+ // the benchmark name
+ std::string name;
+ // the benchmark fields
+ std::map<std::string, std::string> values;
+
+ Results(const std::string& n) : name(n) {}
+
+ int NumThreads() const;
+
+ double NumIterations() const;
+
+ typedef enum { kCpuTime, kRealTime } BenchmarkTime;
+
+ // get cpu_time or real_time in seconds
+ double GetTime(BenchmarkTime which) const;
+
+ // get the real_time duration of the benchmark in seconds.
+ // it is better to use fuzzy float checks for this, as the float
+ // ASCII formatting is lossy.
+ double DurationRealTime() const {
+ return NumIterations() * GetTime(kRealTime);
+ }
+ // get the cpu_time duration of the benchmark in seconds
+ double DurationCPUTime() const {
+ return NumIterations() * GetTime(kCpuTime);
+ }
+
+ // get the string for a result by name, or nullptr if the name
+ // is not found
+ const std::string* Get(const char* entry_name) const {
+ auto it = values.find(entry_name);
+ if (it == values.end()) return nullptr;
+ return &it->second;
+ }
+
+ // get a result by name, parsed as a specific type.
+ // NOTE: for counters, use GetCounterAs instead.
+ template <class T>
+ T GetAs(const char* entry_name) const;
+
+ // counters are written as doubles, so they have to be read first
+ // as a double, and only then converted to the asked type.
+ template <class T>
+ T GetCounterAs(const char* entry_name) const {
+ double dval = GetAs<double>(entry_name);
+ T tval = static_cast<T>(dval);
+ return tval;
+ }
+};
+
+template <class T>
+T Results::GetAs(const char* entry_name) const {
+ auto* sv = Get(entry_name);
+ CHECK(sv != nullptr && !sv->empty());
+ std::stringstream ss;
+ ss << *sv;
+ T out;
+ ss >> out;
+ CHECK(!ss.fail());
+ return out;
+}
+
+//----------------------------------
+// Macros to help in result checking. Do not use them with arguments causing
+// side-effects.
+
+// clang-format off
+
+#define CHECK_RESULT_VALUE_IMPL(entry, getfn, var_type, var_name, relationship, value) \
+ CONCAT(CHECK_, relationship) \
+ (entry.getfn< var_type >(var_name), (value)) << "\n" \
+ << __FILE__ << ":" << __LINE__ << ": " << (entry).name << ":\n" \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << "expected (" << #var_type << ")" << (var_name) \
+ << "=" << (entry).getfn< var_type >(var_name) \
+ << " to be " #relationship " to " << (value) << "\n"
+
+// check with tolerance. eps_factor is the tolerance window, which is
+// interpreted relative to value (eg, 0.1 means 10% of value).
+#define CHECK_FLOAT_RESULT_VALUE_IMPL(entry, getfn, var_type, var_name, relationship, value, eps_factor) \
+ CONCAT(CHECK_FLOAT_, relationship) \
+ (entry.getfn< var_type >(var_name), (value), (eps_factor) * (value)) << "\n" \
+ << __FILE__ << ":" << __LINE__ << ": " << (entry).name << ":\n" \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << "expected (" << #var_type << ")" << (var_name) \
+ << "=" << (entry).getfn< var_type >(var_name) \
+ << " to be " #relationship " to " << (value) << "\n" \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << "with tolerance of " << (eps_factor) * (value) \
+ << " (" << (eps_factor)*100. << "%), " \
+ << "but delta was " << ((entry).getfn< var_type >(var_name) - (value)) \
+ << " (" << (((entry).getfn< var_type >(var_name) - (value)) \
+ / \
+ ((value) > 1.e-5 || value < -1.e-5 ? value : 1.e-5)*100.) \
+ << "%)"
+
+#define CHECK_RESULT_VALUE(entry, var_type, var_name, relationship, value) \
+ CHECK_RESULT_VALUE_IMPL(entry, GetAs, var_type, var_name, relationship, value)
+
+#define CHECK_COUNTER_VALUE(entry, var_type, var_name, relationship, value) \
+ CHECK_RESULT_VALUE_IMPL(entry, GetCounterAs, var_type, var_name, relationship, value)
+
+#define CHECK_FLOAT_RESULT_VALUE(entry, var_name, relationship, value, eps_factor) \
+ CHECK_FLOAT_RESULT_VALUE_IMPL(entry, GetAs, double, var_name, relationship, value, eps_factor)
+
+#define CHECK_FLOAT_COUNTER_VALUE(entry, var_name, relationship, value, eps_factor) \
+ CHECK_FLOAT_RESULT_VALUE_IMPL(entry, GetCounterAs, double, var_name, relationship, value, eps_factor)
+
+// clang-format on
+
+// ========================================================================= //
+// --------------------------- Misc Utilities ------------------------------ //
+// ========================================================================= //
+
+namespace {
+
+const char* const dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
+
+} // end namespace
+
+#endif // TEST_OUTPUT_TEST_H
--- /dev/null
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <random>
+#include <sstream>
+#include <streambuf>
+
+#include "../src/benchmark_api_internal.h"
+#include "../src/check.h" // NOTE: check.h is for internal use only!
+#include "../src/re.h" // NOTE: re.h is for internal use only
+#include "output_test.h"
+
+// ========================================================================= //
+// ------------------------------ Internals -------------------------------- //
+// ========================================================================= //
+namespace internal {
+namespace {
+
+using TestCaseList = std::vector<TestCase>;
+
+// Use a vector because the order elements are added matters during iteration.
+// std::map/unordered_map don't guarantee that.
+// For example:
+// SetSubstitutions({{"%HelloWorld", "Hello"}, {"%Hello", "Hi"}});
+// Substitute("%HelloWorld") // Always expands to Hello.
+using SubMap = std::vector<std::pair<std::string, std::string>>;
+
+TestCaseList& GetTestCaseList(TestCaseID ID) {
+ // Uses function-local statics to ensure initialization occurs
+ // before first use.
+ static TestCaseList lists[TC_NumID];
+ return lists[ID];
+}
+
+SubMap& GetSubstitutions() {
+ // Don't use 'dec_re' from header because it may not yet be initialized.
+ // clang-format off
+ static std::string safe_dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
+ static std::string time_re = "([0-9]+[.])?[0-9]+";
+ static SubMap map = {
+ {"%float", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?"},
+ // human-readable float
+ {"%hrfloat", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?[kMGTPEZYmunpfazy]?"},
+ {"%int", "[ ]*[0-9]+"},
+ {" %s ", "[ ]+"},
+ {"%time", "[ ]*" + time_re + "[ ]+ns"},
+ {"%console_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns [ ]*[0-9]+"},
+ {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"},
+ {"%console_ms_report", "[ ]*" + time_re + "[ ]+ms [ ]*" + time_re + "[ ]+ms [ ]*[0-9]+"},
+ {"%console_s_report", "[ ]*" + time_re + "[ ]+s [ ]*" + time_re + "[ ]+s [ ]*[0-9]+"},
+ {"%console_time_only_report", "[ ]*" + time_re + "[ ]+ns [ ]*" + time_re + "[ ]+ns"},
+ {"%console_us_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us [ ]*[0-9]+"},
+ {"%console_us_time_only_report", "[ ]*" + time_re + "[ ]+us [ ]*" + time_re + "[ ]+us"},
+ {"%csv_header",
+ "name,iterations,real_time,cpu_time,time_unit,bytes_per_second,"
+ "items_per_second,label,error_occurred,error_message"},
+ {"%csv_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,,,"},
+ {"%csv_us_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",us,,,,,"},
+ {"%csv_ms_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ms,,,,,"},
+ {"%csv_s_report", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",s,,,,,"},
+ {"%csv_bytes_report",
+ "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re + ",,,,"},
+ {"%csv_items_report",
+ "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,," + safe_dec_re + ",,,"},
+ {"%csv_bytes_items_report",
+ "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns," + safe_dec_re +
+ "," + safe_dec_re + ",,,"},
+ {"%csv_label_report_begin", "[0-9]+," + safe_dec_re + "," + safe_dec_re + ",ns,,,"},
+ {"%csv_label_report_end", ",,"}};
+ // clang-format on
+ return map;
+}
+
+std::string PerformSubstitutions(std::string source) {
+ SubMap const& subs = GetSubstitutions();
+ using SizeT = std::string::size_type;
+ for (auto const& KV : subs) {
+ SizeT pos;
+ SizeT next_start = 0;
+ while ((pos = source.find(KV.first, next_start)) != std::string::npos) {
+ next_start = pos + KV.second.size();
+ source.replace(pos, KV.first.size(), KV.second);
+ }
+ }
+ return source;
+}
+
+void CheckCase(std::stringstream& remaining_output, TestCase const& TC,
+ TestCaseList const& not_checks) {
+ std::string first_line;
+ bool on_first = true;
+ std::string line;
+ while (remaining_output.eof() == false) {
+ CHECK(remaining_output.good());
+ std::getline(remaining_output, line);
+ if (on_first) {
+ first_line = line;
+ on_first = false;
+ }
+ for (const auto& NC : not_checks) {
+ CHECK(!NC.regex->Match(line))
+ << "Unexpected match for line \"" << line << "\" for MR_Not regex \""
+ << NC.regex_str << "\""
+ << "\n actual regex string \"" << TC.substituted_regex << "\""
+ << "\n started matching near: " << first_line;
+ }
+ if (TC.regex->Match(line)) return;
+ CHECK(TC.match_rule != MR_Next)
+ << "Expected line \"" << line << "\" to match regex \"" << TC.regex_str
+ << "\""
+ << "\n actual regex string \"" << TC.substituted_regex << "\""
+ << "\n started matching near: " << first_line;
+ }
+ CHECK(remaining_output.eof() == false)
+ << "End of output reached before match for regex \"" << TC.regex_str
+ << "\" was found"
+ << "\n actual regex string \"" << TC.substituted_regex << "\""
+ << "\n started matching near: " << first_line;
+}
+
+void CheckCases(TestCaseList const& checks, std::stringstream& output) {
+ std::vector<TestCase> not_checks;
+ for (size_t i = 0; i < checks.size(); ++i) {
+ const auto& TC = checks[i];
+ if (TC.match_rule == MR_Not) {
+ not_checks.push_back(TC);
+ continue;
+ }
+ CheckCase(output, TC, not_checks);
+ not_checks.clear();
+ }
+}
+
+class TestReporter : public benchmark::BenchmarkReporter {
+ public:
+ TestReporter(std::vector<benchmark::BenchmarkReporter*> reps)
+ : reporters_(reps) {}
+
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE {
+ bool last_ret = false;
+ bool first = true;
+ for (auto rep : reporters_) {
+ bool new_ret = rep->ReportContext(context);
+ CHECK(first || new_ret == last_ret)
+ << "Reports return different values for ReportContext";
+ first = false;
+ last_ret = new_ret;
+ }
+ (void)first;
+ return last_ret;
+ }
+
+ void ReportRuns(const std::vector<Run>& report) BENCHMARK_OVERRIDE {
+ for (auto rep : reporters_) rep->ReportRuns(report);
+ }
+ void Finalize() BENCHMARK_OVERRIDE {
+ for (auto rep : reporters_) rep->Finalize();
+ }
+
+ private:
+ std::vector<benchmark::BenchmarkReporter*> reporters_;
+};
+} // namespace
+
+} // end namespace internal
+
+// ========================================================================= //
+// -------------------------- Results checking ----------------------------- //
+// ========================================================================= //
+
+namespace internal {
+
+// Utility class to manage subscribers for checking benchmark results.
+// It works by parsing the CSV output to read the results.
+class ResultsChecker {
+ public:
+ struct PatternAndFn : public TestCase { // reusing TestCase for its regexes
+ PatternAndFn(const std::string& rx, ResultsCheckFn fn_)
+ : TestCase(rx), fn(fn_) {}
+ ResultsCheckFn fn;
+ };
+
+ std::vector<PatternAndFn> check_patterns;
+ std::vector<Results> results;
+ std::vector<std::string> field_names;
+
+ void Add(const std::string& entry_pattern, ResultsCheckFn fn);
+
+ void CheckResults(std::stringstream& output);
+
+ private:
+ void SetHeader_(const std::string& csv_header);
+ void SetValues_(const std::string& entry_csv_line);
+
+ std::vector<std::string> SplitCsv_(const std::string& line);
+};
+
+// store the static ResultsChecker in a function to prevent initialization
+// order problems
+ResultsChecker& GetResultsChecker() {
+ static ResultsChecker rc;
+ return rc;
+}
+
+// add a results checker for a benchmark
+void ResultsChecker::Add(const std::string& entry_pattern, ResultsCheckFn fn) {
+ check_patterns.emplace_back(entry_pattern, fn);
+}
+
+// check the results of all subscribed benchmarks
+void ResultsChecker::CheckResults(std::stringstream& output) {
+ // first reset the stream to the start
+ {
+ auto start = std::stringstream::pos_type(0);
+ // clear before calling tellg()
+ output.clear();
+ // seek to zero only when needed
+ if (output.tellg() > start) output.seekg(start);
+ // and just in case
+ output.clear();
+ }
+ // now go over every line and publish it to the ResultsChecker
+ std::string line;
+ bool on_first = true;
+ while (output.eof() == false) {
+ CHECK(output.good());
+ std::getline(output, line);
+ if (on_first) {
+ SetHeader_(line); // this is important
+ on_first = false;
+ continue;
+ }
+ SetValues_(line);
+ }
+ // finally we can call the subscribed check functions
+ for (const auto& p : check_patterns) {
+ VLOG(2) << "--------------------------------\n";
+ VLOG(2) << "checking for benchmarks matching " << p.regex_str << "...\n";
+ for (const auto& r : results) {
+ if (!p.regex->Match(r.name)) {
+ VLOG(2) << p.regex_str << " is not matched by " << r.name << "\n";
+ continue;
+ } else {
+ VLOG(2) << p.regex_str << " is matched by " << r.name << "\n";
+ }
+ VLOG(1) << "Checking results of " << r.name << ": ... \n";
+ p.fn(r);
+ VLOG(1) << "Checking results of " << r.name << ": OK.\n";
+ }
+ }
+}
+
+// prepare for the names in this header
+void ResultsChecker::SetHeader_(const std::string& csv_header) {
+ field_names = SplitCsv_(csv_header);
+}
+
+// set the values for a benchmark
+void ResultsChecker::SetValues_(const std::string& entry_csv_line) {
+ if (entry_csv_line.empty()) return; // some lines are empty
+ CHECK(!field_names.empty());
+ auto vals = SplitCsv_(entry_csv_line);
+ CHECK_EQ(vals.size(), field_names.size());
+ results.emplace_back(vals[0]); // vals[0] is the benchmark name
+ auto& entry = results.back();
+ for (size_t i = 1, e = vals.size(); i < e; ++i) {
+ entry.values[field_names[i]] = vals[i];
+ }
+}
+
+// a quick'n'dirty csv splitter (eliminating quotes)
+std::vector<std::string> ResultsChecker::SplitCsv_(const std::string& line) {
+ std::vector<std::string> out;
+ if (line.empty()) return out;
+ if (!field_names.empty()) out.reserve(field_names.size());
+ size_t prev = 0, pos = line.find_first_of(','), curr = pos;
+ while (pos != line.npos) {
+ CHECK(curr > 0);
+ if (line[prev] == '"') ++prev;
+ if (line[curr - 1] == '"') --curr;
+ out.push_back(line.substr(prev, curr - prev));
+ prev = pos + 1;
+ pos = line.find_first_of(',', pos + 1);
+ curr = pos;
+ }
+ curr = line.size();
+ if (line[prev] == '"') ++prev;
+ if (line[curr - 1] == '"') --curr;
+ out.push_back(line.substr(prev, curr - prev));
+ return out;
+}
+
+} // end namespace internal
+
+size_t AddChecker(const char* bm_name, ResultsCheckFn fn) {
+ auto& rc = internal::GetResultsChecker();
+ rc.Add(bm_name, fn);
+ return rc.results.size();
+}
+
+int Results::NumThreads() const {
+ auto pos = name.find("/threads:");
+ if (pos == name.npos) return 1;
+ auto end = name.find('/', pos + 9);
+ std::stringstream ss;
+ ss << name.substr(pos + 9, end);
+ int num = 1;
+ ss >> num;
+ CHECK(!ss.fail());
+ return num;
+}
+
+double Results::NumIterations() const {
+ return GetAs<double>("iterations");
+}
+
+double Results::GetTime(BenchmarkTime which) const {
+ CHECK(which == kCpuTime || which == kRealTime);
+ const char* which_str = which == kCpuTime ? "cpu_time" : "real_time";
+ double val = GetAs<double>(which_str);
+ auto unit = Get("time_unit");
+ CHECK(unit);
+ if (*unit == "ns") {
+ return val * 1.e-9;
+ } else if (*unit == "us") {
+ return val * 1.e-6;
+ } else if (*unit == "ms") {
+ return val * 1.e-3;
+ } else if (*unit == "s") {
+ return val;
+ } else {
+ CHECK(1 == 0) << "unknown time unit: " << *unit;
+ return 0;
+ }
+}
+
+// ========================================================================= //
+// -------------------------- Public API Definitions------------------------ //
+// ========================================================================= //
+
+TestCase::TestCase(std::string re, int rule)
+ : regex_str(std::move(re)),
+ match_rule(rule),
+ substituted_regex(internal::PerformSubstitutions(regex_str)),
+ regex(std::make_shared<benchmark::Regex>()) {
+ std::string err_str;
+ regex->Init(substituted_regex, &err_str);
+ CHECK(err_str.empty()) << "Could not construct regex \"" << substituted_regex
+ << "\""
+ << "\n originally \"" << regex_str << "\""
+ << "\n got error: " << err_str;
+}
+
+int AddCases(TestCaseID ID, std::initializer_list<TestCase> il) {
+ auto& L = internal::GetTestCaseList(ID);
+ L.insert(L.end(), il);
+ return 0;
+}
+
+int SetSubstitutions(
+ std::initializer_list<std::pair<std::string, std::string>> il) {
+ auto& subs = internal::GetSubstitutions();
+ for (auto KV : il) {
+ bool exists = false;
+ KV.second = internal::PerformSubstitutions(KV.second);
+ for (auto& EKV : subs) {
+ if (EKV.first == KV.first) {
+ EKV.second = std::move(KV.second);
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) subs.push_back(std::move(KV));
+ }
+ return 0;
+}
+
+// Disable deprecated warnings temporarily because we need to reference
+// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+void RunOutputTests(int argc, char* argv[]) {
+ using internal::GetTestCaseList;
+ benchmark::Initialize(&argc, argv);
+ auto options = benchmark::internal::GetOutputOptions(/*force_no_color*/ true);
+ benchmark::ConsoleReporter CR(options);
+ benchmark::JSONReporter JR;
+ benchmark::CSVReporter CSVR;
+ struct ReporterTest {
+ const char* name;
+ std::vector<TestCase>& output_cases;
+ std::vector<TestCase>& error_cases;
+ benchmark::BenchmarkReporter& reporter;
+ std::stringstream out_stream;
+ std::stringstream err_stream;
+
+ ReporterTest(const char* n, std::vector<TestCase>& out_tc,
+ std::vector<TestCase>& err_tc,
+ benchmark::BenchmarkReporter& br)
+ : name(n), output_cases(out_tc), error_cases(err_tc), reporter(br) {
+ reporter.SetOutputStream(&out_stream);
+ reporter.SetErrorStream(&err_stream);
+ }
+ } TestCases[] = {
+ {"ConsoleReporter", GetTestCaseList(TC_ConsoleOut),
+ GetTestCaseList(TC_ConsoleErr), CR},
+ {"JSONReporter", GetTestCaseList(TC_JSONOut), GetTestCaseList(TC_JSONErr),
+ JR},
+ {"CSVReporter", GetTestCaseList(TC_CSVOut), GetTestCaseList(TC_CSVErr),
+ CSVR},
+ };
+
+ // Create the test reporter and run the benchmarks.
+ std::cout << "Running benchmarks...\n";
+ internal::TestReporter test_rep({&CR, &JR, &CSVR});
+ benchmark::RunSpecifiedBenchmarks(&test_rep);
+
+ for (auto& rep_test : TestCases) {
+ std::string msg = std::string("\nTesting ") + rep_test.name + " Output\n";
+ std::string banner(msg.size() - 1, '-');
+ std::cout << banner << msg << banner << "\n";
+
+ std::cerr << rep_test.err_stream.str();
+ std::cout << rep_test.out_stream.str();
+
+ internal::CheckCases(rep_test.error_cases, rep_test.err_stream);
+ internal::CheckCases(rep_test.output_cases, rep_test.out_stream);
+
+ std::cout << "\n";
+ }
+
+ // now that we know the output is as expected, we can dispatch
+ // the checks to subscribees.
+ auto& csv = TestCases[2];
+ // would use == but gcc spits a warning
+ CHECK(std::strcmp(csv.name, "CSVReporter") == 0);
+ internal::GetResultsChecker().CheckResults(csv.out_stream);
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+int SubstrCnt(const std::string& haystack, const std::string& pat) {
+ if (pat.length() == 0) return 0;
+ int count = 0;
+ for (size_t offset = haystack.find(pat); offset != std::string::npos;
+ offset = haystack.find(pat, offset + pat.length()))
+ ++count;
+ return count;
+}
+
+static char ToHex(int ch) {
+ return ch < 10 ? static_cast<char>('0' + ch)
+ : static_cast<char>('a' + (ch - 10));
+}
+
+static char RandomHexChar() {
+ static std::mt19937 rd{std::random_device{}()};
+ static std::uniform_int_distribution<int> mrand{0, 15};
+ return ToHex(mrand(rd));
+}
+
+static std::string GetRandomFileName() {
+ std::string model = "test.%%%%%%";
+ for (auto & ch : model) {
+ if (ch == '%')
+ ch = RandomHexChar();
+ }
+ return model;
+}
+
+static bool FileExists(std::string const& name) {
+ std::ifstream in(name.c_str());
+ return in.good();
+}
+
+static std::string GetTempFileName() {
+ // This function attempts to avoid race conditions where two tests
+ // create the same file at the same time. However, it still introduces races
+ // similar to tmpnam.
+ int retries = 3;
+ while (--retries) {
+ std::string name = GetRandomFileName();
+ if (!FileExists(name))
+ return name;
+ }
+ std::cerr << "Failed to create unique temporary file name" << std::endl;
+ std::abort();
+}
+
+std::string GetFileReporterOutput(int argc, char* argv[]) {
+ std::vector<char*> new_argv(argv, argv + argc);
+ assert(static_cast<decltype(new_argv)::size_type>(argc) == new_argv.size());
+
+ std::string tmp_file_name = GetTempFileName();
+ std::cout << "Will be using this as the tmp file: " << tmp_file_name << '\n';
+
+ std::string tmp = "--benchmark_out=";
+ tmp += tmp_file_name;
+ new_argv.emplace_back(const_cast<char*>(tmp.c_str()));
+
+ argc = int(new_argv.size());
+
+ benchmark::Initialize(&argc, new_argv.data());
+ benchmark::RunSpecifiedBenchmarks();
+
+ // Read the output back from the file, and delete the file.
+ std::ifstream tmp_stream(tmp_file_name);
+ std::string output = std::string((std::istreambuf_iterator<char>(tmp_stream)),
+ std::istreambuf_iterator<char>());
+ std::remove(tmp_file_name.c_str());
+
+ return output;
+}
--- /dev/null
+#include <thread>
+
+#include "../src/perf_counters.h"
+#include "gtest/gtest.h"
+
+#ifndef GTEST_SKIP
+struct MsgHandler {
+ void operator=(std::ostream&){}
+};
+#define GTEST_SKIP() return MsgHandler() = std::cout
+#endif
+
+using benchmark::internal::PerfCounters;
+using benchmark::internal::PerfCounterValues;
+
+namespace {
+const char kGenericPerfEvent1[] = "CYCLES";
+const char kGenericPerfEvent2[] = "BRANCHES";
+const char kGenericPerfEvent3[] = "INSTRUCTIONS";
+
+TEST(PerfCountersTest, Init) {
+ EXPECT_EQ(PerfCounters::Initialize(), PerfCounters::kSupported);
+}
+
+TEST(PerfCountersTest, OneCounter) {
+ if (!PerfCounters::kSupported) {
+ GTEST_SKIP() << "Performance counters not supported.\n";
+ }
+ EXPECT_TRUE(PerfCounters::Initialize());
+ EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1}).IsValid());
+}
+
+TEST(PerfCountersTest, NegativeTest) {
+ if (!PerfCounters::kSupported) {
+ EXPECT_FALSE(PerfCounters::Initialize());
+ return;
+ }
+ EXPECT_TRUE(PerfCounters::Initialize());
+ EXPECT_FALSE(PerfCounters::Create({}).IsValid());
+ EXPECT_FALSE(PerfCounters::Create({""}).IsValid());
+ EXPECT_FALSE(PerfCounters::Create({"not a counter name"}).IsValid());
+ {
+ EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
+ kGenericPerfEvent3})
+ .IsValid());
+ }
+ EXPECT_FALSE(
+ PerfCounters::Create({kGenericPerfEvent2, "", kGenericPerfEvent1})
+ .IsValid());
+ EXPECT_FALSE(PerfCounters::Create({kGenericPerfEvent3, "not a counter name",
+ kGenericPerfEvent1})
+ .IsValid());
+ {
+ EXPECT_TRUE(PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
+ kGenericPerfEvent3})
+ .IsValid());
+ }
+ EXPECT_FALSE(
+ PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2,
+ kGenericPerfEvent3, "MISPREDICTED_BRANCH_RETIRED"})
+ .IsValid());
+}
+
+TEST(PerfCountersTest, Read1Counter) {
+ if (!PerfCounters::kSupported) {
+ GTEST_SKIP() << "Test skipped because libpfm is not supported.\n";
+ }
+ EXPECT_TRUE(PerfCounters::Initialize());
+ auto counters = PerfCounters::Create({kGenericPerfEvent1});
+ EXPECT_TRUE(counters.IsValid());
+ PerfCounterValues values1(1);
+ EXPECT_TRUE(counters.Snapshot(&values1));
+ EXPECT_GT(values1[0], 0);
+ PerfCounterValues values2(1);
+ EXPECT_TRUE(counters.Snapshot(&values2));
+ EXPECT_GT(values2[0], 0);
+ EXPECT_GT(values2[0], values1[0]);
+}
+
+TEST(PerfCountersTest, Read2Counters) {
+ if (!PerfCounters::kSupported) {
+ GTEST_SKIP() << "Test skipped because libpfm is not supported.\n";
+ }
+ EXPECT_TRUE(PerfCounters::Initialize());
+ auto counters =
+ PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent2});
+ EXPECT_TRUE(counters.IsValid());
+ PerfCounterValues values1(2);
+ EXPECT_TRUE(counters.Snapshot(&values1));
+ EXPECT_GT(values1[0], 0);
+ EXPECT_GT(values1[1], 0);
+ PerfCounterValues values2(2);
+ EXPECT_TRUE(counters.Snapshot(&values2));
+ EXPECT_GT(values2[0], 0);
+ EXPECT_GT(values2[1], 0);
+}
+
+size_t do_work() {
+ size_t res = 0;
+ for (size_t i = 0; i < 100000000; ++i) res += i * i;
+ return res;
+}
+
+void measure(size_t threadcount, PerfCounterValues* values1,
+ PerfCounterValues* values2) {
+ CHECK_NE(values1, nullptr);
+ CHECK_NE(values2, nullptr);
+ std::vector<std::thread> threads(threadcount);
+ auto work = [&]() { CHECK(do_work() > 1000); };
+
+ // We need to first set up the counters, then start the threads, so the
+ // threads would inherit the counters. But later, we need to first destroy the
+ // thread pool (so all the work finishes), then measure the counters. So the
+ // scopes overlap, and we need to explicitly control the scope of the
+ // threadpool.
+ auto counters =
+ PerfCounters::Create({kGenericPerfEvent1, kGenericPerfEvent3});
+ for (auto& t : threads) t = std::thread(work);
+ counters.Snapshot(values1);
+ for (auto& t : threads) t.join();
+ counters.Snapshot(values2);
+}
+
+TEST(PerfCountersTest, MultiThreaded) {
+ if (!PerfCounters::kSupported) {
+ GTEST_SKIP() << "Test skipped because libpfm is not supported.";
+ }
+ EXPECT_TRUE(PerfCounters::Initialize());
+ PerfCounterValues values1(2);
+ PerfCounterValues values2(2);
+
+ measure(2, &values1, &values2);
+ std::vector<double> D1{static_cast<double>(values2[0] - values1[0]),
+ static_cast<double>(values2[1] - values1[1])};
+
+ measure(4, &values1, &values2);
+ std::vector<double> D2{static_cast<double>(values2[0] - values1[0]),
+ static_cast<double>(values2[1] - values1[1])};
+
+ // Some extra work will happen on the main thread - like joining the threads
+ // - so the ratio won't be quite 2.0, but very close.
+ EXPECT_GE(D2[0], 1.9 * D1[0]);
+ EXPECT_GE(D2[1], 1.9 * D1[1]);
+}
+} // namespace
--- /dev/null
+#undef NDEBUG
+
+#include "../src/perf_counters.h"
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+void BM_Simple(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+}
+BENCHMARK(BM_Simple);
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Simple\",$"}});
+
+void CheckSimple(Results const& e) {
+ CHECK_COUNTER_VALUE(e, double, "CYCLES", GT, 0);
+ CHECK_COUNTER_VALUE(e, double, "BRANCHES", GT, 0.0);
+}
+CHECK_BENCHMARK_RESULTS("BM_Simple", &CheckSimple);
+
+int main(int argc, char* argv[]) {
+ if (!benchmark::internal::PerfCounters::kSupported) {
+ return 0;
+ }
+ RunOutputTests(argc, argv);
+}
--- /dev/null
+
+#undef NDEBUG
+#include <cassert>
+#include <vector>
+
+#include "../src/check.h" // NOTE: check.h is for internal use only!
+#include "benchmark/benchmark.h"
+
+namespace {
+
+class TestReporter : public benchmark::ConsoleReporter {
+ public:
+ virtual void ReportRuns(const std::vector<Run>& report) BENCHMARK_OVERRIDE {
+ all_runs_.insert(all_runs_.end(), begin(report), end(report));
+ ConsoleReporter::ReportRuns(report);
+ }
+
+ std::vector<Run> all_runs_;
+};
+
+struct TestCase {
+ std::string name;
+ const char* label;
+ // Note: not explicit as we rely on it being converted through ADD_CASES.
+ TestCase(const char* xname) : TestCase(xname, nullptr) {}
+ TestCase(const char* xname, const char* xlabel)
+ : name(xname), label(xlabel) {}
+
+ typedef benchmark::BenchmarkReporter::Run Run;
+
+ void CheckRun(Run const& run) const {
+ // clang-format off
+ CHECK(name == run.benchmark_name()) << "expected " << name << " got "
+ << run.benchmark_name();
+ if (label) {
+ CHECK(run.report_label == label) << "expected " << label << " got "
+ << run.report_label;
+ } else {
+ CHECK(run.report_label == "");
+ }
+ // clang-format on
+ }
+};
+
+std::vector<TestCase> ExpectedResults;
+
+int AddCases(std::initializer_list<TestCase> const& v) {
+ for (auto N : v) {
+ ExpectedResults.push_back(N);
+ }
+ return 0;
+}
+
+#define CONCAT(x, y) CONCAT2(x, y)
+#define CONCAT2(x, y) x##y
+#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = AddCases({__VA_ARGS__})
+
+} // end namespace
+
+typedef benchmark::internal::Benchmark* ReturnVal;
+
+//----------------------------------------------------------------------------//
+// Test RegisterBenchmark with no additional arguments
+//----------------------------------------------------------------------------//
+void BM_function(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_function);
+ReturnVal dummy = benchmark::RegisterBenchmark(
+ "BM_function_manual_registration", BM_function);
+ADD_CASES({"BM_function"}, {"BM_function_manual_registration"});
+
+//----------------------------------------------------------------------------//
+// Test RegisterBenchmark with additional arguments
+// Note: GCC <= 4.8 do not support this form of RegisterBenchmark because they
+// reject the variadic pack expansion of lambda captures.
+//----------------------------------------------------------------------------//
+#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
+
+void BM_extra_args(benchmark::State& st, const char* label) {
+ for (auto _ : st) {
+ }
+ st.SetLabel(label);
+}
+int RegisterFromFunction() {
+ std::pair<const char*, const char*> cases[] = {
+ {"test1", "One"}, {"test2", "Two"}, {"test3", "Three"}};
+ for (auto const& c : cases)
+ benchmark::RegisterBenchmark(c.first, &BM_extra_args, c.second);
+ return 0;
+}
+int dummy2 = RegisterFromFunction();
+ADD_CASES({"test1", "One"}, {"test2", "Two"}, {"test3", "Three"});
+
+#endif // BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
+
+//----------------------------------------------------------------------------//
+// Test RegisterBenchmark with different callable types
+//----------------------------------------------------------------------------//
+
+struct CustomFixture {
+ void operator()(benchmark::State& st) {
+ for (auto _ : st) {
+ }
+ }
+};
+
+void TestRegistrationAtRuntime() {
+#ifdef BENCHMARK_HAS_CXX11
+ {
+ CustomFixture fx;
+ benchmark::RegisterBenchmark("custom_fixture", fx);
+ AddCases({"custom_fixture"});
+ }
+#endif
+#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
+ {
+ const char* x = "42";
+ auto capturing_lam = [=](benchmark::State& st) {
+ for (auto _ : st) {
+ }
+ st.SetLabel(x);
+ };
+ benchmark::RegisterBenchmark("lambda_benchmark", capturing_lam);
+ AddCases({{"lambda_benchmark", x}});
+ }
+#endif
+}
+
+// Test that all benchmarks, registered at either during static init or runtime,
+// are run and the results are passed to the reported.
+void RunTestOne() {
+ TestRegistrationAtRuntime();
+
+ TestReporter test_reporter;
+ benchmark::RunSpecifiedBenchmarks(&test_reporter);
+
+ typedef benchmark::BenchmarkReporter::Run Run;
+ auto EB = ExpectedResults.begin();
+
+ for (Run const& run : test_reporter.all_runs_) {
+ assert(EB != ExpectedResults.end());
+ EB->CheckRun(run);
+ ++EB;
+ }
+ assert(EB == ExpectedResults.end());
+}
+
+// Test that ClearRegisteredBenchmarks() clears all previously registered
+// benchmarks.
+// Also test that new benchmarks can be registered and ran afterwards.
+void RunTestTwo() {
+ assert(ExpectedResults.size() != 0 &&
+ "must have at least one registered benchmark");
+ ExpectedResults.clear();
+ benchmark::ClearRegisteredBenchmarks();
+
+ TestReporter test_reporter;
+ size_t num_ran = benchmark::RunSpecifiedBenchmarks(&test_reporter);
+ assert(num_ran == 0);
+ assert(test_reporter.all_runs_.begin() == test_reporter.all_runs_.end());
+
+ TestRegistrationAtRuntime();
+ num_ran = benchmark::RunSpecifiedBenchmarks(&test_reporter);
+ assert(num_ran == ExpectedResults.size());
+
+ typedef benchmark::BenchmarkReporter::Run Run;
+ auto EB = ExpectedResults.begin();
+
+ for (Run const& run : test_reporter.all_runs_) {
+ assert(EB != ExpectedResults.end());
+ EB->CheckRun(run);
+ ++EB;
+ }
+ assert(EB == ExpectedResults.end());
+}
+
+int main(int argc, char* argv[]) {
+ benchmark::Initialize(&argc, argv);
+
+ RunTestOne();
+ RunTestTwo();
+}
--- /dev/null
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// ========================================================================= //
+// ------------------------ Testing Basic Output --------------------------- //
+// ========================================================================= //
+
+void BM_ExplicitRepetitions(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_ExplicitRepetitions)->Repetitions(2);
+
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_ExplicitRepetitions/repeats:2 %console_report$"}});
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_ExplicitRepetitions/repeats:2 %console_report$"}});
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_ExplicitRepetitions/repeats:2_mean %console_report$"}});
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_ExplicitRepetitions/repeats:2_median %console_report$"}});
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_ExplicitRepetitions/repeats:2_stddev %console_report$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_ExplicitRepetitions/repeats:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ExplicitRepetitions/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_ExplicitRepetitions/repeats:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ExplicitRepetitions/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_ExplicitRepetitions/repeats:2_mean\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ExplicitRepetitions/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_ExplicitRepetitions/repeats:2_median\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ExplicitRepetitions/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_ExplicitRepetitions/repeats:2_stddev\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ExplicitRepetitions/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ExplicitRepetitions/repeats:2\",%csv_report$"}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ExplicitRepetitions/repeats:2\",%csv_report$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_ExplicitRepetitions/repeats:2_mean\",%csv_report$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_ExplicitRepetitions/repeats:2_median\",%csv_report$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_ExplicitRepetitions/repeats:2_stddev\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Basic Output --------------------------- //
+// ========================================================================= //
+
+void BM_ImplicitRepetitions(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_ImplicitRepetitions);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions %console_report$"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions %console_report$"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions %console_report$"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions_mean %console_report$"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions_median %console_report$"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_ImplicitRepetitions_stddev %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions_mean\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions_median\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_ImplicitRepetitions_stddev\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_ImplicitRepetitions\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions\",%csv_report$"}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions\",%csv_report$"}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_mean\",%csv_report$"}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_median\",%csv_report$"}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_ImplicitRepetitions_stddev\",%csv_report$"}});
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+
+#undef NDEBUG
+#include <cstdio>
+#include <string>
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// Ok this test is super ugly. We want to check what happens with the file
+// reporter in the presence of ReportAggregatesOnly().
+// We do not care about console output, the normal tests check that already.
+
+void BM_SummaryRepeat(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_SummaryRepeat)->Repetitions(3)->ReportAggregatesOnly();
+
+int main(int argc, char* argv[]) {
+ const std::string output = GetFileReporterOutput(argc, argv);
+
+ if (SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3") != 3 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_mean\"") != 1 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_median\"") !=
+ 1 ||
+ SubstrCnt(output, "\"name\": \"BM_SummaryRepeat/repeats:3_stddev\"") !=
+ 1) {
+ std::cout << "Precondition mismatch. Expected to only find three "
+ "occurrences of \"BM_SummaryRepeat/repeats:3\" substring:\n"
+ "\"name\": \"BM_SummaryRepeat/repeats:3_mean\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3_median\", "
+ "\"name\": \"BM_SummaryRepeat/repeats:3_stddev\"\nThe entire "
+ "output:\n";
+ std::cout << output;
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+
+#undef NDEBUG
+#include <utility>
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// ========================================================================= //
+// ---------------------- Testing Prologue Output -------------------------- //
+// ========================================================================= //
+
+ADD_CASES(TC_ConsoleOut, {{"^[-]+$", MR_Next},
+ {"^Benchmark %s Time %s CPU %s Iterations$", MR_Next},
+ {"^[-]+$", MR_Next}});
+static int AddContextCases() {
+ AddCases(TC_ConsoleErr,
+ {
+ {"^%int-%int-%intT%int:%int:%int[-+]%int:%int$", MR_Default},
+ {"Running .*/reporter_output_test(\\.exe)?$", MR_Next},
+ {"Run on \\(%int X %float MHz CPU s?\\)", MR_Next},
+ });
+ AddCases(TC_JSONOut,
+ {{"^\\{", MR_Default},
+ {"\"context\":", MR_Next},
+ {"\"date\": \"", MR_Next},
+ {"\"host_name\":", MR_Next},
+ {"\"executable\": \".*(/|\\\\)reporter_output_test(\\.exe)?\",",
+ MR_Next},
+ {"\"num_cpus\": %int,$", MR_Next},
+ {"\"mhz_per_cpu\": %float,$", MR_Next},
+ {"\"caches\": \\[$", MR_Default}});
+ auto const& Info = benchmark::CPUInfo::Get();
+ auto const& Caches = Info.caches;
+ if (!Caches.empty()) {
+ AddCases(TC_ConsoleErr, {{"CPU Caches:$", MR_Next}});
+ }
+ for (size_t I = 0; I < Caches.size(); ++I) {
+ std::string num_caches_str =
+ Caches[I].num_sharing != 0 ? " \\(x%int\\)$" : "$";
+ AddCases(TC_ConsoleErr,
+ {{"L%int (Data|Instruction|Unified) %int KiB" + num_caches_str,
+ MR_Next}});
+ AddCases(TC_JSONOut, {{"\\{$", MR_Next},
+ {"\"type\": \"", MR_Next},
+ {"\"level\": %int,$", MR_Next},
+ {"\"size\": %int,$", MR_Next},
+ {"\"num_sharing\": %int$", MR_Next},
+ {"}[,]{0,1}$", MR_Next}});
+ }
+ AddCases(TC_JSONOut, {{"],$"}});
+ auto const& LoadAvg = Info.load_avg;
+ if (!LoadAvg.empty()) {
+ AddCases(TC_ConsoleErr,
+ {{"Load Average: (%float, ){0,2}%float$", MR_Next}});
+ }
+ AddCases(TC_JSONOut, {{"\"load_avg\": \\[(%float,?){0,3}],$", MR_Next}});
+ return 0;
+}
+int dummy_register = AddContextCases();
+ADD_CASES(TC_CSVOut, {{"%csv_header"}});
+
+// ========================================================================= //
+// ------------------------ Testing Basic Output --------------------------- //
+// ========================================================================= //
+
+void BM_basic(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_basic);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_basic %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_basic\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_basic\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_basic\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Bytes per Second Output ---------------- //
+// ========================================================================= //
+
+void BM_bytes_per_second(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ state.SetBytesProcessed(1);
+}
+BENCHMARK(BM_bytes_per_second);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_bytes_per_second %console_report "
+ "bytes_per_second=%float[kM]{0,1}/s$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_bytes_per_second\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_bytes_per_second\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bytes_per_second\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_bytes_per_second\",%csv_bytes_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Items per Second Output ---------------- //
+// ========================================================================= //
+
+void BM_items_per_second(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ state.SetItemsProcessed(1);
+}
+BENCHMARK(BM_items_per_second);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_items_per_second %console_report "
+ "items_per_second=%float[kM]{0,1}/s$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_items_per_second\",$"},
+ {"\"family_index\": 2,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_items_per_second\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"items_per_second\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_items_per_second\",%csv_items_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Label Output --------------------------- //
+// ========================================================================= //
+
+void BM_label(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ state.SetLabel("some label");
+}
+BENCHMARK(BM_label);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_label %console_report some label$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_label\",$"},
+ {"\"family_index\": 3,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_label\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"label\": \"some label\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_label\",%csv_label_report_begin\"some "
+ "label\"%csv_label_report_end$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Time Label Output ---------------------- //
+// ========================================================================= //
+
+void BM_time_label_nanosecond(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_time_label_nanosecond)->Unit(benchmark::kNanosecond);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_time_label_nanosecond %console_report$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_time_label_nanosecond\",$"},
+ {"\"family_index\": 4,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_time_label_nanosecond\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_time_label_nanosecond\",%csv_report$"}});
+
+void BM_time_label_microsecond(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_time_label_microsecond)->Unit(benchmark::kMicrosecond);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_time_label_microsecond %console_us_report$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_time_label_microsecond\",$"},
+ {"\"family_index\": 5,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_time_label_microsecond\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"us\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_time_label_microsecond\",%csv_us_report$"}});
+
+void BM_time_label_millisecond(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_time_label_millisecond)->Unit(benchmark::kMillisecond);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_time_label_millisecond %console_ms_report$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_time_label_millisecond\",$"},
+ {"\"family_index\": 6,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_time_label_millisecond\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ms\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_time_label_millisecond\",%csv_ms_report$"}});
+
+void BM_time_label_second(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_time_label_second)->Unit(benchmark::kSecond);
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_time_label_second %console_s_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_time_label_second\",$"},
+ {"\"family_index\": 7,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_time_label_second\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"s\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_time_label_second\",%csv_s_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Error Output --------------------------- //
+// ========================================================================= //
+
+void BM_error(benchmark::State& state) {
+ state.SkipWithError("message");
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_error);
+ADD_CASES(TC_ConsoleOut, {{"^BM_error[ ]+ERROR OCCURRED: 'message'$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_error\",$"},
+ {"\"family_index\": 8,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_error\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"error_occurred\": true,$", MR_Next},
+ {"\"error_message\": \"message\",$", MR_Next}});
+
+ADD_CASES(TC_CSVOut, {{"^\"BM_error\",,,,,,,,true,\"message\"$"}});
+
+// ========================================================================= //
+// ------------------------ Testing No Arg Name Output -----------------------
+// //
+// ========================================================================= //
+
+void BM_no_arg_name(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_no_arg_name)->Arg(3);
+ADD_CASES(TC_ConsoleOut, {{"^BM_no_arg_name/3 %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_no_arg_name/3\",$"},
+ {"\"family_index\": 9,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_no_arg_name/3\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_no_arg_name/3\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Arg Name Output ----------------------- //
+// ========================================================================= //
+
+void BM_arg_name(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_arg_name)->ArgName("first")->Arg(3);
+ADD_CASES(TC_ConsoleOut, {{"^BM_arg_name/first:3 %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_arg_name/first:3\",$"},
+ {"\"family_index\": 10,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_arg_name/first:3\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_arg_name/first:3\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Arg Names Output ----------------------- //
+// ========================================================================= //
+
+void BM_arg_names(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_arg_names)->Args({2, 5, 4})->ArgNames({"first", "", "third"});
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_arg_names/first:2/5/third:4 %console_report$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_arg_names/first:2/5/third:4\",$"},
+ {"\"family_index\": 11,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_arg_names/first:2/5/third:4\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_arg_names/first:2/5/third:4\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Name Output ---------------------------- //
+// ========================================================================= //
+
+void BM_name(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_name)->Name("BM_custom_name");
+
+ADD_CASES(TC_ConsoleOut, {{"^BM_custom_name %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_custom_name\",$"},
+ {"\"family_index\": 12,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_custom_name\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\"$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_custom_name\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------ Testing Big Args Output ------------------------ //
+// ========================================================================= //
+
+void BM_BigArgs(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_BigArgs)->RangeMultiplier(2)->Range(1U << 30U, 1U << 31U);
+ADD_CASES(TC_ConsoleOut, {{"^BM_BigArgs/1073741824 %console_report$"},
+ {"^BM_BigArgs/2147483648 %console_report$"}});
+
+// ========================================================================= //
+// ----------------------- Testing Complexity Output ----------------------- //
+// ========================================================================= //
+
+void BM_Complexity_O1(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ state.SetComplexityN(state.range(0));
+}
+BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity(benchmark::o1);
+SET_SUBSTITUTIONS({{"%bigOStr", "[ ]* %float \\([0-9]+\\)"},
+ {"%RMS", "[ ]*[0-9]+ %"}});
+ADD_CASES(TC_ConsoleOut, {{"^BM_Complexity_O1_BigO %bigOStr %bigOStr[ ]*$"},
+ {"^BM_Complexity_O1_RMS %RMS %RMS[ ]*$"}});
+
+// ========================================================================= //
+// ----------------------- Testing Aggregate Output ------------------------ //
+// ========================================================================= //
+
+// Test that non-aggregate data is printed by default
+void BM_Repeat(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+// need two repetitions min to be able to output any aggregate output
+BENCHMARK(BM_Repeat)->Repetitions(2);
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_Repeat/repeats:2 %console_report$"},
+ {"^BM_Repeat/repeats:2 %console_report$"},
+ {"^BM_Repeat/repeats:2_mean %console_time_only_report [ ]*2$"},
+ {"^BM_Repeat/repeats:2_median %console_time_only_report [ ]*2$"},
+ {"^BM_Repeat/repeats:2_stddev %console_time_only_report [ ]*2$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Repeat/repeats:2\",$"},
+ {"\"family_index\": 15,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:2\"", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:2\",$"},
+ {"\"family_index\": 15,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:2_mean\",$"},
+ {"\"family_index\": 15,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:2_median\",$"},
+ {"\"family_index\": 15,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:2_stddev\",$"},
+ {"\"family_index\": 15,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Repeat/repeats:2\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:2\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:2_mean\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:2_median\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:2_stddev\",%csv_report$"}});
+// but for two repetitions, mean and median is the same, so let's repeat..
+BENCHMARK(BM_Repeat)->Repetitions(3);
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_Repeat/repeats:3 %console_report$"},
+ {"^BM_Repeat/repeats:3 %console_report$"},
+ {"^BM_Repeat/repeats:3 %console_report$"},
+ {"^BM_Repeat/repeats:3_mean %console_time_only_report [ ]*3$"},
+ {"^BM_Repeat/repeats:3_median %console_time_only_report [ ]*3$"},
+ {"^BM_Repeat/repeats:3_stddev %console_time_only_report [ ]*3$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Repeat/repeats:3\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:3\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:3\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:3_mean\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:3_median\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:3_stddev\",$"},
+ {"\"family_index\": 16,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Repeat/repeats:3\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:3\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:3\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:3_mean\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:3_median\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:3_stddev\",%csv_report$"}});
+// median differs between even/odd number of repetitions, so just to be sure
+BENCHMARK(BM_Repeat)->Repetitions(4);
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_Repeat/repeats:4 %console_report$"},
+ {"^BM_Repeat/repeats:4 %console_report$"},
+ {"^BM_Repeat/repeats:4 %console_report$"},
+ {"^BM_Repeat/repeats:4 %console_report$"},
+ {"^BM_Repeat/repeats:4_mean %console_time_only_report [ ]*4$"},
+ {"^BM_Repeat/repeats:4_median %console_time_only_report [ ]*4$"},
+ {"^BM_Repeat/repeats:4_stddev %console_time_only_report [ ]*4$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Repeat/repeats:4\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"repetition_index\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"repetition_index\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4_mean\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 4,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4_median\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 4,$", MR_Next},
+ {"\"name\": \"BM_Repeat/repeats:4_stddev\",$"},
+ {"\"family_index\": 17,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Repeat/repeats:4\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 4,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 4,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Repeat/repeats:4\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4_mean\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4_median\",%csv_report$"},
+ {"^\"BM_Repeat/repeats:4_stddev\",%csv_report$"}});
+
+// Test that a non-repeated test still prints non-aggregate results even when
+// only-aggregate reports have been requested
+void BM_RepeatOnce(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_RepeatOnce)->Repetitions(1)->ReportAggregatesOnly();
+ADD_CASES(TC_ConsoleOut, {{"^BM_RepeatOnce/repeats:1 %console_report$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_RepeatOnce/repeats:1\",$"},
+ {"\"family_index\": 18,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_RepeatOnce/repeats:1\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_RepeatOnce/repeats:1\",%csv_report$"}});
+
+// Test that non-aggregate data is not reported
+void BM_SummaryRepeat(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_SummaryRepeat)->Repetitions(3)->ReportAggregatesOnly();
+ADD_CASES(
+ TC_ConsoleOut,
+ {{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
+ {"^BM_SummaryRepeat/repeats:3_mean %console_time_only_report [ ]*3$"},
+ {"^BM_SummaryRepeat/repeats:3_median %console_time_only_report [ ]*3$"},
+ {"^BM_SummaryRepeat/repeats:3_stddev %console_time_only_report [ ]*3$"}});
+ADD_CASES(TC_JSONOut,
+ {{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
+ {"\"name\": \"BM_SummaryRepeat/repeats:3_mean\",$"},
+ {"\"family_index\": 19,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryRepeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"name\": \"BM_SummaryRepeat/repeats:3_median\",$"},
+ {"\"family_index\": 19,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryRepeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"name\": \"BM_SummaryRepeat/repeats:3_stddev\",$"},
+ {"\"family_index\": 19,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryRepeat/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
+ {"^\"BM_SummaryRepeat/repeats:3_mean\",%csv_report$"},
+ {"^\"BM_SummaryRepeat/repeats:3_median\",%csv_report$"},
+ {"^\"BM_SummaryRepeat/repeats:3_stddev\",%csv_report$"}});
+
+// Test that non-aggregate data is not displayed.
+// NOTE: this test is kinda bad. we are only testing the display output.
+// But we don't check that the file output still contains everything...
+void BM_SummaryDisplay(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_SummaryDisplay)->Repetitions(2)->DisplayAggregatesOnly();
+ADD_CASES(
+ TC_ConsoleOut,
+ {{".*BM_SummaryDisplay/repeats:2 ", MR_Not},
+ {"^BM_SummaryDisplay/repeats:2_mean %console_time_only_report [ ]*2$"},
+ {"^BM_SummaryDisplay/repeats:2_median %console_time_only_report [ ]*2$"},
+ {"^BM_SummaryDisplay/repeats:2_stddev %console_time_only_report [ ]*2$"}});
+ADD_CASES(TC_JSONOut,
+ {{".*BM_SummaryDisplay/repeats:2 ", MR_Not},
+ {"\"name\": \"BM_SummaryDisplay/repeats:2_mean\",$"},
+ {"\"family_index\": 20,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryDisplay/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"name\": \"BM_SummaryDisplay/repeats:2_median\",$"},
+ {"\"family_index\": 20,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryDisplay/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"name\": \"BM_SummaryDisplay/repeats:2_stddev\",$"},
+ {"\"family_index\": 20,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_SummaryDisplay/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next}});
+ADD_CASES(TC_CSVOut,
+ {{".*BM_SummaryDisplay/repeats:2 ", MR_Not},
+ {"^\"BM_SummaryDisplay/repeats:2_mean\",%csv_report$"},
+ {"^\"BM_SummaryDisplay/repeats:2_median\",%csv_report$"},
+ {"^\"BM_SummaryDisplay/repeats:2_stddev\",%csv_report$"}});
+
+// Test repeats with custom time unit.
+void BM_RepeatTimeUnit(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_RepeatTimeUnit)
+ ->Repetitions(3)
+ ->ReportAggregatesOnly()
+ ->Unit(benchmark::kMicrosecond);
+ADD_CASES(
+ TC_ConsoleOut,
+ {{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
+ {"^BM_RepeatTimeUnit/repeats:3_mean %console_us_time_only_report [ ]*3$"},
+ {"^BM_RepeatTimeUnit/repeats:3_median %console_us_time_only_report [ "
+ "]*3$"},
+ {"^BM_RepeatTimeUnit/repeats:3_stddev %console_us_time_only_report [ "
+ "]*3$"}});
+ADD_CASES(TC_JSONOut,
+ {{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
+ {"\"name\": \"BM_RepeatTimeUnit/repeats:3_mean\",$"},
+ {"\"family_index\": 21,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_RepeatTimeUnit/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"time_unit\": \"us\",?$"},
+ {"\"name\": \"BM_RepeatTimeUnit/repeats:3_median\",$"},
+ {"\"family_index\": 21,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_RepeatTimeUnit/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"time_unit\": \"us\",?$"},
+ {"\"name\": \"BM_RepeatTimeUnit/repeats:3_stddev\",$"},
+ {"\"family_index\": 21,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_RepeatTimeUnit/repeats:3\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"time_unit\": \"us\",?$"}});
+ADD_CASES(TC_CSVOut,
+ {{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
+ {"^\"BM_RepeatTimeUnit/repeats:3_mean\",%csv_us_report$"},
+ {"^\"BM_RepeatTimeUnit/repeats:3_median\",%csv_us_report$"},
+ {"^\"BM_RepeatTimeUnit/repeats:3_stddev\",%csv_us_report$"}});
+
+// ========================================================================= //
+// -------------------- Testing user-provided statistics ------------------- //
+// ========================================================================= //
+
+const auto UserStatistics = [](const std::vector<double>& v) {
+ return v.back();
+};
+void BM_UserStats(benchmark::State& state) {
+ for (auto _ : state) {
+ state.SetIterationTime(150 / 10e8);
+ }
+}
+// clang-format off
+BENCHMARK(BM_UserStats)
+ ->Repetitions(3)
+ ->Iterations(5)
+ ->UseManualTime()
+ ->ComputeStatistics("", UserStatistics);
+// clang-format on
+
+// check that user-provided stats is calculated, and is after the default-ones
+// empty string as name is intentional, it would sort before anything else
+ADD_CASES(TC_ConsoleOut, {{"^BM_UserStats/iterations:5/repeats:3/manual_time [ "
+ "]* 150 ns %time [ ]*5$"},
+ {"^BM_UserStats/iterations:5/repeats:3/manual_time [ "
+ "]* 150 ns %time [ ]*5$"},
+ {"^BM_UserStats/iterations:5/repeats:3/manual_time [ "
+ "]* 150 ns %time [ ]*5$"},
+ {"^BM_UserStats/iterations:5/repeats:3/"
+ "manual_time_mean [ ]* 150 ns %time [ ]*3$"},
+ {"^BM_UserStats/iterations:5/repeats:3/"
+ "manual_time_median [ ]* 150 ns %time [ ]*3$"},
+ {"^BM_UserStats/iterations:5/repeats:3/"
+ "manual_time_stddev [ ]* 0.000 ns %time [ ]*3$"},
+ {"^BM_UserStats/iterations:5/repeats:3/manual_time_ "
+ "[ ]* 150 ns %time [ ]*3$"}});
+ADD_CASES(
+ TC_JSONOut,
+ {{"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": 5,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": 5,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"repetition_index\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": 5,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time_mean\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time_median\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time_stddev\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"name\": \"BM_UserStats/iterations:5/repeats:3/manual_time_\",$"},
+ {"\"family_index\": 22,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_UserStats/iterations:5/repeats:3/manual_time\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 3,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"\",$", MR_Next},
+ {"\"iterations\": 3,$", MR_Next},
+ {"\"real_time\": 1\\.5(0)*e\\+(0)*2,$", MR_Next}});
+ADD_CASES(
+ TC_CSVOut,
+ {{"^\"BM_UserStats/iterations:5/repeats:3/manual_time\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/manual_time\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/manual_time\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/manual_time_mean\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/"
+ "manual_time_median\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/"
+ "manual_time_stddev\",%csv_report$"},
+ {"^\"BM_UserStats/iterations:5/repeats:3/manual_time_\",%csv_report$"}});
+
+// ========================================================================= //
+// ------------------------- Testing StrEscape JSON ------------------------ //
+// ========================================================================= //
+#if 0 // enable when csv testing code correctly handles multi-line fields
+void BM_JSON_Format(benchmark::State& state) {
+ state.SkipWithError("val\b\f\n\r\t\\\"with\"es,capes");
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_JSON_Format);
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_JSON_Format\",$"},
+ {"\"family_index\": 23,$", MR_Next},
+{"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_JSON_Format\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"error_occurred\": true,$", MR_Next},
+ {R"("error_message": "val\\b\\f\\n\\r\\t\\\\\\"with\\"es,capes",$)", MR_Next}});
+#endif
+// ========================================================================= //
+// -------------------------- Testing CsvEscape ---------------------------- //
+// ========================================================================= //
+
+void BM_CSV_Format(benchmark::State& state) {
+ state.SkipWithError("\"freedom\"");
+ for (auto _ : state) {
+ }
+}
+BENCHMARK(BM_CSV_Format);
+ADD_CASES(TC_CSVOut, {{"^\"BM_CSV_Format\",,,,,,,,true,\"\"\"freedom\"\"\"$"}});
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+
+#undef NDEBUG
+#include <cassert>
+#include <vector>
+
+#include "../src/check.h" // NOTE: check.h is for internal use only!
+#include "benchmark/benchmark.h"
+
+namespace {
+
+class TestReporter : public benchmark::ConsoleReporter {
+ public:
+ virtual bool ReportContext(const Context& context) BENCHMARK_OVERRIDE {
+ return ConsoleReporter::ReportContext(context);
+ };
+
+ virtual void ReportRuns(const std::vector<Run>& report) BENCHMARK_OVERRIDE {
+ all_runs_.insert(all_runs_.end(), begin(report), end(report));
+ ConsoleReporter::ReportRuns(report);
+ }
+
+ TestReporter() {}
+ virtual ~TestReporter() {}
+
+ mutable std::vector<Run> all_runs_;
+};
+
+struct TestCase {
+ std::string name;
+ bool error_occurred;
+ std::string error_message;
+
+ typedef benchmark::BenchmarkReporter::Run Run;
+
+ void CheckRun(Run const& run) const {
+ CHECK(name == run.benchmark_name())
+ << "expected " << name << " got " << run.benchmark_name();
+ CHECK(error_occurred == run.error_occurred);
+ CHECK(error_message == run.error_message);
+ if (error_occurred) {
+ // CHECK(run.iterations == 0);
+ } else {
+ CHECK(run.iterations != 0);
+ }
+ }
+};
+
+std::vector<TestCase> ExpectedResults;
+
+int AddCases(const char* base_name, std::initializer_list<TestCase> const& v) {
+ for (auto TC : v) {
+ TC.name = base_name + TC.name;
+ ExpectedResults.push_back(std::move(TC));
+ }
+ return 0;
+}
+
+#define CONCAT(x, y) CONCAT2(x, y)
+#define CONCAT2(x, y) x##y
+#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = AddCases(__VA_ARGS__)
+
+} // end namespace
+
+void BM_error_no_running(benchmark::State& state) {
+ state.SkipWithError("error message");
+}
+BENCHMARK(BM_error_no_running);
+ADD_CASES("BM_error_no_running", {{"", true, "error message"}});
+
+void BM_error_before_running(benchmark::State& state) {
+ state.SkipWithError("error message");
+ while (state.KeepRunning()) {
+ assert(false);
+ }
+}
+BENCHMARK(BM_error_before_running);
+ADD_CASES("BM_error_before_running", {{"", true, "error message"}});
+
+void BM_error_before_running_batch(benchmark::State& state) {
+ state.SkipWithError("error message");
+ while (state.KeepRunningBatch(17)) {
+ assert(false);
+ }
+}
+BENCHMARK(BM_error_before_running_batch);
+ADD_CASES("BM_error_before_running_batch", {{"", true, "error message"}});
+
+void BM_error_before_running_range_for(benchmark::State& state) {
+ state.SkipWithError("error message");
+ for (auto _ : state) {
+ assert(false);
+ }
+}
+BENCHMARK(BM_error_before_running_range_for);
+ADD_CASES("BM_error_before_running_range_for", {{"", true, "error message"}});
+
+void BM_error_during_running(benchmark::State& state) {
+ int first_iter = true;
+ while (state.KeepRunning()) {
+ if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
+ assert(first_iter);
+ first_iter = false;
+ state.SkipWithError("error message");
+ } else {
+ state.PauseTiming();
+ state.ResumeTiming();
+ }
+ }
+}
+BENCHMARK(BM_error_during_running)->Arg(1)->Arg(2)->ThreadRange(1, 8);
+ADD_CASES("BM_error_during_running", {{"/1/threads:1", true, "error message"},
+ {"/1/threads:2", true, "error message"},
+ {"/1/threads:4", true, "error message"},
+ {"/1/threads:8", true, "error message"},
+ {"/2/threads:1", false, ""},
+ {"/2/threads:2", false, ""},
+ {"/2/threads:4", false, ""},
+ {"/2/threads:8", false, ""}});
+
+void BM_error_during_running_ranged_for(benchmark::State& state) {
+ assert(state.max_iterations > 3 && "test requires at least a few iterations");
+ int first_iter = true;
+ // NOTE: Users should not write the for loop explicitly.
+ for (auto It = state.begin(), End = state.end(); It != End; ++It) {
+ if (state.range(0) == 1) {
+ assert(first_iter);
+ first_iter = false;
+ state.SkipWithError("error message");
+ // Test the unfortunate but documented behavior that the ranged-for loop
+ // doesn't automatically terminate when SkipWithError is set.
+ assert(++It != End);
+ break; // Required behavior
+ }
+ }
+}
+BENCHMARK(BM_error_during_running_ranged_for)->Arg(1)->Arg(2)->Iterations(5);
+ADD_CASES("BM_error_during_running_ranged_for",
+ {{"/1/iterations:5", true, "error message"},
+ {"/2/iterations:5", false, ""}});
+
+void BM_error_after_running(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ if (state.thread_index <= (state.threads / 2))
+ state.SkipWithError("error message");
+}
+BENCHMARK(BM_error_after_running)->ThreadRange(1, 8);
+ADD_CASES("BM_error_after_running", {{"/threads:1", true, "error message"},
+ {"/threads:2", true, "error message"},
+ {"/threads:4", true, "error message"},
+ {"/threads:8", true, "error message"}});
+
+void BM_error_while_paused(benchmark::State& state) {
+ bool first_iter = true;
+ while (state.KeepRunning()) {
+ if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
+ assert(first_iter);
+ first_iter = false;
+ state.PauseTiming();
+ state.SkipWithError("error message");
+ } else {
+ state.PauseTiming();
+ state.ResumeTiming();
+ }
+ }
+}
+BENCHMARK(BM_error_while_paused)->Arg(1)->Arg(2)->ThreadRange(1, 8);
+ADD_CASES("BM_error_while_paused", {{"/1/threads:1", true, "error message"},
+ {"/1/threads:2", true, "error message"},
+ {"/1/threads:4", true, "error message"},
+ {"/1/threads:8", true, "error message"},
+ {"/2/threads:1", false, ""},
+ {"/2/threads:2", false, ""},
+ {"/2/threads:4", false, ""},
+ {"/2/threads:8", false, ""}});
+
+int main(int argc, char* argv[]) {
+ benchmark::Initialize(&argc, argv);
+
+ TestReporter test_reporter;
+ benchmark::RunSpecifiedBenchmarks(&test_reporter);
+
+ typedef benchmark::BenchmarkReporter::Run Run;
+ auto EB = ExpectedResults.begin();
+
+ for (Run const& run : test_reporter.all_runs_) {
+ assert(EB != ExpectedResults.end());
+ EB->CheckRun(run);
+ ++EB;
+ }
+ assert(EB == ExpectedResults.end());
+
+ return 0;
+}
--- /dev/null
+#include <benchmark/benchmark.h>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wreturn-type"
+#endif
+
+// clang-format off
+extern "C" {
+ extern int ExternInt;
+ benchmark::State& GetState();
+ void Fn();
+}
+// clang-format on
+
+using benchmark::State;
+
+// CHECK-LABEL: test_for_auto_loop:
+extern "C" int test_for_auto_loop() {
+ State& S = GetState();
+ int x = 42;
+ // CHECK: [[CALL:call(q)*]] _ZN9benchmark5State16StartKeepRunningEv
+ // CHECK-NEXT: testq %rbx, %rbx
+ // CHECK-NEXT: je [[LOOP_END:.*]]
+
+ for (auto _ : S) {
+ // CHECK: .L[[LOOP_HEAD:[a-zA-Z0-9_]+]]:
+ // CHECK-GNU-NEXT: subq $1, %rbx
+ // CHECK-CLANG-NEXT: {{(addq \$1, %rax|incq %rax|addq \$-1, %rbx)}}
+ // CHECK-NEXT: jne .L[[LOOP_HEAD]]
+ benchmark::DoNotOptimize(x);
+ }
+ // CHECK: [[LOOP_END]]:
+ // CHECK: [[CALL]] _ZN9benchmark5State17FinishKeepRunningEv
+
+ // CHECK: movl $101, %eax
+ // CHECK: ret
+ return 101;
+}
+
+// CHECK-LABEL: test_while_loop:
+extern "C" int test_while_loop() {
+ State& S = GetState();
+ int x = 42;
+
+ // CHECK: j{{(e|mp)}} .L[[LOOP_HEADER:[a-zA-Z0-9_]+]]
+ // CHECK-NEXT: .L[[LOOP_BODY:[a-zA-Z0-9_]+]]:
+ while (S.KeepRunning()) {
+ // CHECK-GNU-NEXT: subq $1, %[[IREG:[a-z]+]]
+ // CHECK-CLANG-NEXT: {{(addq \$-1,|decq)}} %[[IREG:[a-z]+]]
+ // CHECK: movq %[[IREG]], [[DEST:.*]]
+ benchmark::DoNotOptimize(x);
+ }
+ // CHECK-DAG: movq [[DEST]], %[[IREG]]
+ // CHECK-DAG: testq %[[IREG]], %[[IREG]]
+ // CHECK-DAG: jne .L[[LOOP_BODY]]
+ // CHECK-DAG: .L[[LOOP_HEADER]]:
+
+ // CHECK: cmpb $0
+ // CHECK-NEXT: jne .L[[LOOP_END:[a-zA-Z0-9_]+]]
+ // CHECK: [[CALL:call(q)*]] _ZN9benchmark5State16StartKeepRunningEv
+
+ // CHECK: .L[[LOOP_END]]:
+ // CHECK: [[CALL]] _ZN9benchmark5State17FinishKeepRunningEv
+
+ // CHECK: movl $101, %eax
+ // CHECK: ret
+ return 101;
+}
--- /dev/null
+//===---------------------------------------------------------------------===//
+// statistics_test - Unit tests for src/statistics.cc
+//===---------------------------------------------------------------------===//
+
+#include "../src/statistics.h"
+#include "gtest/gtest.h"
+
+namespace {
+TEST(StatisticsTest, Mean) {
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMean({42, 42, 42, 42}), 42.0);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMean({1, 2, 3, 4}), 2.5);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMean({1, 2, 5, 10, 10, 14}), 7.0);
+}
+
+TEST(StatisticsTest, Median) {
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMedian({42, 42, 42, 42}), 42.0);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMedian({1, 2, 3, 4}), 2.5);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsMedian({1, 2, 5, 10, 10}), 5.0);
+}
+
+TEST(StatisticsTest, StdDev) {
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsStdDev({101, 101, 101, 101}), 0.0);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsStdDev({1, 2, 3}), 1.0);
+ EXPECT_DOUBLE_EQ(benchmark::StatisticsStdDev({2.5, 2.4, 3.3, 4.2, 5.1}),
+ 1.151086443322134);
+}
+
+} // end namespace
--- /dev/null
+//===---------------------------------------------------------------------===//
+// statistics_test - Unit tests for src/statistics.cc
+//===---------------------------------------------------------------------===//
+
+#include "../src/string_util.h"
+#include "../src/internal_macros.h"
+#include "gtest/gtest.h"
+
+namespace {
+TEST(StringUtilTest, stoul) {
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0ul, benchmark::stoul("0", &pos));
+ EXPECT_EQ(1ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(7ul, benchmark::stoul("7", &pos));
+ EXPECT_EQ(1ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(135ul, benchmark::stoul("135", &pos));
+ EXPECT_EQ(3ul, pos);
+ }
+#if ULONG_MAX == 0xFFFFFFFFul
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0xFFFFFFFFul, benchmark::stoul("4294967295", &pos));
+ EXPECT_EQ(10ul, pos);
+ }
+#elif ULONG_MAX == 0xFFFFFFFFFFFFFFFFul
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0xFFFFFFFFFFFFFFFFul, benchmark::stoul("18446744073709551615", &pos));
+ EXPECT_EQ(20ul, pos);
+ }
+#endif
+ {
+ size_t pos = 0;
+ EXPECT_EQ(10ul, benchmark::stoul("1010", &pos, 2));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(520ul, benchmark::stoul("1010", &pos, 8));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(1010ul, benchmark::stoul("1010", &pos, 10));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(4112ul, benchmark::stoul("1010", &pos, 16));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0xBEEFul, benchmark::stoul("BEEF", &pos, 16));
+ EXPECT_EQ(4ul, pos);
+ }
+#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
+ {
+ ASSERT_THROW(benchmark::stoul("this is a test"), std::invalid_argument);
+ }
+#endif
+}
+
+TEST(StringUtilTest, stoi) {
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0, benchmark::stoi("0", &pos));
+ EXPECT_EQ(1ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(-17, benchmark::stoi("-17", &pos));
+ EXPECT_EQ(3ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(1357, benchmark::stoi("1357", &pos));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(10, benchmark::stoi("1010", &pos, 2));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(520, benchmark::stoi("1010", &pos, 8));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(1010, benchmark::stoi("1010", &pos, 10));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(4112, benchmark::stoi("1010", &pos, 16));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0xBEEF, benchmark::stoi("BEEF", &pos, 16));
+ EXPECT_EQ(4ul, pos);
+ }
+#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
+ {
+ ASSERT_THROW(benchmark::stoi("this is a test"), std::invalid_argument);
+ }
+#endif
+}
+
+TEST(StringUtilTest, stod) {
+ {
+ size_t pos = 0;
+ EXPECT_EQ(0.0, benchmark::stod("0", &pos));
+ EXPECT_EQ(1ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(-84.0, benchmark::stod("-84", &pos));
+ EXPECT_EQ(3ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(1234.0, benchmark::stod("1234", &pos));
+ EXPECT_EQ(4ul, pos);
+ }
+ {
+ size_t pos = 0;
+ EXPECT_EQ(1.5, benchmark::stod("1.5", &pos));
+ EXPECT_EQ(3ul, pos);
+ }
+ {
+ size_t pos = 0;
+ /* Note: exactly representable as double */
+ EXPECT_EQ(-1.25e+9, benchmark::stod("-1.25e+9", &pos));
+ EXPECT_EQ(8ul, pos);
+ }
+#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
+ {
+ ASSERT_THROW(benchmark::stod("this is a test"), std::invalid_argument);
+ }
+#endif
+}
+
+TEST(StringUtilTest, StrSplit) {
+ EXPECT_EQ(benchmark::StrSplit("", ','), std::vector<std::string>{});
+ EXPECT_EQ(benchmark::StrSplit("hello", ','),
+ std::vector<std::string>({"hello"}));
+ EXPECT_EQ(benchmark::StrSplit("hello,there,is,more", ','),
+ std::vector<std::string>({"hello", "there", "is", "more"}));
+}
+
+} // end namespace
--- /dev/null
+
+#include "benchmark/benchmark.h"
+
+#include <cassert>
+#include <memory>
+
+template <typename T>
+class MyFixture : public ::benchmark::Fixture {
+ public:
+ MyFixture() : data(0) {}
+
+ T data;
+};
+
+BENCHMARK_TEMPLATE_F(MyFixture, Foo, int)(benchmark::State& st) {
+ for (auto _ : st) {
+ data += 1;
+ }
+}
+
+BENCHMARK_TEMPLATE_DEFINE_F(MyFixture, Bar, double)(benchmark::State& st) {
+ for (auto _ : st) {
+ data += 1.0;
+ }
+}
+BENCHMARK_REGISTER_F(MyFixture, Bar);
+
+BENCHMARK_MAIN();
--- /dev/null
+
+#undef NDEBUG
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// @todo: <jpmag> this checks the full output at once; the rule for
+// CounterSet1 was failing because it was not matching "^[-]+$".
+// @todo: <jpmag> check that the counters are vertically aligned.
+ADD_CASES(TC_ConsoleOut,
+ {
+ // keeping these lines long improves readability, so:
+ // clang-format off
+ {"^[-]+$", MR_Next},
+ {"^Benchmark %s Time %s CPU %s Iterations %s Bar %s Bat %s Baz %s Foo %s Frob %s Lob$", MR_Next},
+ {"^[-]+$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:1 %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:1 %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:1_mean %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:1_median %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:1_stddev %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:2 %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:2 %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:2_mean %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:2_median %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_Counters_Tabular/repeats:2/threads:2_stddev %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterRates_Tabular/threads:%int %console_report [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s$", MR_Next},
+ {"^BM_CounterRates_Tabular/threads:%int %console_report [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s$", MR_Next},
+ {"^BM_CounterRates_Tabular/threads:%int %console_report [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s$", MR_Next},
+ {"^BM_CounterRates_Tabular/threads:%int %console_report [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s$", MR_Next},
+ {"^BM_CounterRates_Tabular/threads:%int %console_report [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s [ ]*%hrfloat/s$", MR_Next},
+ {"^[-]+$", MR_Next},
+ {"^Benchmark %s Time %s CPU %s Iterations %s Bar %s Baz %s Foo$", MR_Next},
+ {"^[-]+$", MR_Next},
+ {"^BM_CounterSet0_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet0_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet0_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet0_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet0_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet1_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet1_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet1_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet1_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet1_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^[-]+$", MR_Next},
+ {"^Benchmark %s Time %s CPU %s Iterations %s Bat %s Baz %s Foo$", MR_Next},
+ {"^[-]+$", MR_Next},
+ {"^BM_CounterSet2_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet2_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet2_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet2_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$", MR_Next},
+ {"^BM_CounterSet2_Tabular/threads:%int %console_report [ ]*%hrfloat [ ]*%hrfloat [ ]*%hrfloat$"},
+ // clang-format on
+ });
+ADD_CASES(TC_CSVOut, {{"%csv_header,"
+ "\"Bar\",\"Bat\",\"Baz\",\"Foo\",\"Frob\",\"Lob\""}});
+
+// ========================================================================= //
+// ------------------------- Tabular Counters Output ----------------------- //
+// ========================================================================= //
+
+void BM_Counters_Tabular(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"Foo", {1, bm::Counter::kAvgThreads}},
+ {"Bar", {2, bm::Counter::kAvgThreads}},
+ {"Baz", {4, bm::Counter::kAvgThreads}},
+ {"Bat", {8, bm::Counter::kAvgThreads}},
+ {"Frob", {16, bm::Counter::kAvgThreads}},
+ {"Lob", {32, bm::Counter::kAvgThreads}},
+ });
+}
+BENCHMARK(BM_Counters_Tabular)->ThreadRange(1, 2)->Repetitions(2);
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:1_mean\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:1_median\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:1_stddev\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:1\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 1,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 2,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 1,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 2,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:2_median\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 1,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 2,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Tabular/repeats:2/threads:2_stddev\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 1,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Tabular/repeats:2/threads:2\",$",
+ MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 2,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:1\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:1\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:1_mean\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:1_median\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:1_stddev\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:2\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:2\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:2_mean\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:2_median\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_Tabular/repeats:2/threads:2_stddev\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckTabular(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "Foo", EQ, 1);
+ CHECK_COUNTER_VALUE(e, int, "Bar", EQ, 2);
+ CHECK_COUNTER_VALUE(e, int, "Baz", EQ, 4);
+ CHECK_COUNTER_VALUE(e, int, "Bat", EQ, 8);
+ CHECK_COUNTER_VALUE(e, int, "Frob", EQ, 16);
+ CHECK_COUNTER_VALUE(e, int, "Lob", EQ, 32);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_Tabular/repeats:2/threads:1$",
+ &CheckTabular);
+CHECK_BENCHMARK_RESULTS("BM_Counters_Tabular/repeats:2/threads:2$",
+ &CheckTabular);
+
+// ========================================================================= //
+// -------------------- Tabular+Rate Counters Output ----------------------- //
+// ========================================================================= //
+
+void BM_CounterRates_Tabular(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"Foo", {1, bm::Counter::kAvgThreadsRate}},
+ {"Bar", {2, bm::Counter::kAvgThreadsRate}},
+ {"Baz", {4, bm::Counter::kAvgThreadsRate}},
+ {"Bat", {8, bm::Counter::kAvgThreadsRate}},
+ {"Frob", {16, bm::Counter::kAvgThreadsRate}},
+ {"Lob", {32, bm::Counter::kAvgThreadsRate}},
+ });
+}
+BENCHMARK(BM_CounterRates_Tabular)->ThreadRange(1, 16);
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_CounterRates_Tabular/threads:%int\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_CounterRates_Tabular/threads:%int\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float,$", MR_Next},
+ {"\"Frob\": %float,$", MR_Next},
+ {"\"Lob\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_CounterRates_Tabular/threads:%int\",%csv_report,"
+ "%float,%float,%float,%float,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckTabularRate(Results const& e) {
+ double t = e.DurationCPUTime();
+ CHECK_FLOAT_COUNTER_VALUE(e, "Foo", EQ, 1. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "Bar", EQ, 2. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "Baz", EQ, 4. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "Bat", EQ, 8. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "Frob", EQ, 16. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "Lob", EQ, 32. / t, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_CounterRates_Tabular/threads:%int",
+ &CheckTabularRate);
+
+// ========================================================================= //
+// ------------------------- Tabular Counters Output ----------------------- //
+// ========================================================================= //
+
+// set only some of the counters
+void BM_CounterSet0_Tabular(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"Foo", {10, bm::Counter::kAvgThreads}},
+ {"Bar", {20, bm::Counter::kAvgThreads}},
+ {"Baz", {40, bm::Counter::kAvgThreads}},
+ });
+}
+BENCHMARK(BM_CounterSet0_Tabular)->ThreadRange(1, 16);
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_CounterSet0_Tabular/threads:%int\",$"},
+ {"\"family_index\": 2,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_CounterSet0_Tabular/threads:%int\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_CounterSet0_Tabular/threads:%int\",%csv_report,"
+ "%float,,%float,%float,,"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckSet0(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "Foo", EQ, 10);
+ CHECK_COUNTER_VALUE(e, int, "Bar", EQ, 20);
+ CHECK_COUNTER_VALUE(e, int, "Baz", EQ, 40);
+}
+CHECK_BENCHMARK_RESULTS("BM_CounterSet0_Tabular", &CheckSet0);
+
+// again.
+void BM_CounterSet1_Tabular(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"Foo", {15, bm::Counter::kAvgThreads}},
+ {"Bar", {25, bm::Counter::kAvgThreads}},
+ {"Baz", {45, bm::Counter::kAvgThreads}},
+ });
+}
+BENCHMARK(BM_CounterSet1_Tabular)->ThreadRange(1, 16);
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_CounterSet1_Tabular/threads:%int\",$"},
+ {"\"family_index\": 3,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_CounterSet1_Tabular/threads:%int\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bar\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_CounterSet1_Tabular/threads:%int\",%csv_report,"
+ "%float,,%float,%float,,"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckSet1(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "Foo", EQ, 15);
+ CHECK_COUNTER_VALUE(e, int, "Bar", EQ, 25);
+ CHECK_COUNTER_VALUE(e, int, "Baz", EQ, 45);
+}
+CHECK_BENCHMARK_RESULTS("BM_CounterSet1_Tabular/threads:%int", &CheckSet1);
+
+// ========================================================================= //
+// ------------------------- Tabular Counters Output ----------------------- //
+// ========================================================================= //
+
+// set only some of the counters, different set now.
+void BM_CounterSet2_Tabular(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"Foo", {10, bm::Counter::kAvgThreads}},
+ {"Bat", {30, bm::Counter::kAvgThreads}},
+ {"Baz", {40, bm::Counter::kAvgThreads}},
+ });
+}
+BENCHMARK(BM_CounterSet2_Tabular)->ThreadRange(1, 16);
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_CounterSet2_Tabular/threads:%int\",$"},
+ {"\"family_index\": 4,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_CounterSet2_Tabular/threads:%int\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"Bat\": %float,$", MR_Next},
+ {"\"Baz\": %float,$", MR_Next},
+ {"\"Foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_CounterSet2_Tabular/threads:%int\",%csv_report,"
+ ",%float,%float,%float,,"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckSet2(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "Foo", EQ, 10);
+ CHECK_COUNTER_VALUE(e, int, "Bat", EQ, 30);
+ CHECK_COUNTER_VALUE(e, int, "Baz", EQ, 40);
+}
+CHECK_BENCHMARK_RESULTS("BM_CounterSet2_Tabular", &CheckSet2);
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+
+#undef NDEBUG
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// ========================================================================= //
+// ---------------------- Testing Prologue Output -------------------------- //
+// ========================================================================= //
+
+// clang-format off
+
+ADD_CASES(TC_ConsoleOut,
+ {{"^[-]+$", MR_Next},
+ {"^Benchmark %s Time %s CPU %s Iterations UserCounters...$", MR_Next},
+ {"^[-]+$", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"%csv_header,\"bar\",\"foo\""}});
+
+// clang-format on
+
+// ========================================================================= //
+// ------------------------- Simple Counters Output ------------------------ //
+// ========================================================================= //
+
+void BM_Counters_Simple(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ state.counters["foo"] = 1;
+ state.counters["bar"] = 2 * (double)state.iterations();
+}
+BENCHMARK(BM_Counters_Simple);
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_Counters_Simple %console_report bar=%hrfloat foo=%hrfloat$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Counters_Simple\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Simple\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_Simple\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckSimple(Results const& e) {
+ double its = e.NumIterations();
+ CHECK_COUNTER_VALUE(e, int, "foo", EQ, 1);
+ // check that the value of bar is within 0.1% of the expected value
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. * its, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_Simple", &CheckSimple);
+
+// ========================================================================= //
+// --------------------- Counters+Items+Bytes/s Output --------------------- //
+// ========================================================================= //
+
+namespace {
+int num_calls1 = 0;
+}
+void BM_Counters_WithBytesAndItemsPSec(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ state.counters["foo"] = 1;
+ state.counters["bar"] = ++num_calls1;
+ state.SetBytesProcessed(364);
+ state.SetItemsProcessed(150);
+}
+BENCHMARK(BM_Counters_WithBytesAndItemsPSec);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_WithBytesAndItemsPSec %console_report "
+ "bar=%hrfloat bytes_per_second=%hrfloat/s "
+ "foo=%hrfloat items_per_second=%hrfloat/s$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_WithBytesAndItemsPSec\",$"},
+ {"\"family_index\": 1,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_WithBytesAndItemsPSec\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"bytes_per_second\": %float,$", MR_Next},
+ {"\"foo\": %float,$", MR_Next},
+ {"\"items_per_second\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_WithBytesAndItemsPSec\","
+ "%csv_bytes_items_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckBytesAndItemsPSec(Results const& e) {
+ double t = e.DurationCPUTime(); // this (and not real time) is the time used
+ CHECK_COUNTER_VALUE(e, int, "foo", EQ, 1);
+ CHECK_COUNTER_VALUE(e, int, "bar", EQ, num_calls1);
+ // check that the values are within 0.1% of the expected values
+ CHECK_FLOAT_RESULT_VALUE(e, "bytes_per_second", EQ, 364. / t, 0.001);
+ CHECK_FLOAT_RESULT_VALUE(e, "items_per_second", EQ, 150. / t, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_WithBytesAndItemsPSec",
+ &CheckBytesAndItemsPSec);
+
+// ========================================================================= //
+// ------------------------- Rate Counters Output -------------------------- //
+// ========================================================================= //
+
+void BM_Counters_Rate(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kIsRate};
+ state.counters["bar"] = bm::Counter{2, bm::Counter::kIsRate};
+}
+BENCHMARK(BM_Counters_Rate);
+ADD_CASES(
+ TC_ConsoleOut,
+ {{"^BM_Counters_Rate %console_report bar=%hrfloat/s foo=%hrfloat/s$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Counters_Rate\",$"},
+ {"\"family_index\": 2,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Rate\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_Rate\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckRate(Results const& e) {
+ double t = e.DurationCPUTime(); // this (and not real time) is the time used
+ // check that the values are within 0.1% of the expected values
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 1. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. / t, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_Rate", &CheckRate);
+
+// ========================================================================= //
+// ----------------------- Inverted Counters Output ------------------------ //
+// ========================================================================= //
+
+void BM_Invert(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{0.0001, bm::Counter::kInvert};
+ state.counters["bar"] = bm::Counter{10000, bm::Counter::kInvert};
+}
+BENCHMARK(BM_Invert);
+ADD_CASES(TC_ConsoleOut,
+ {{"^BM_Invert %console_report bar=%hrfloatu foo=%hrfloatk$"}});
+ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Invert\",$"},
+ {"\"family_index\": 3,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Invert\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Invert\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckInvert(Results const& e) {
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 10000, 0.0001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 0.0001, 0.0001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Invert", &CheckInvert);
+
+// ========================================================================= //
+// ------------------------- InvertedRate Counters Output
+// -------------------------- //
+// ========================================================================= //
+
+void BM_Counters_InvertedRate(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] =
+ bm::Counter{1, bm::Counter::kIsRate | bm::Counter::kInvert};
+ state.counters["bar"] =
+ bm::Counter{8192, bm::Counter::kIsRate | bm::Counter::kInvert};
+}
+BENCHMARK(BM_Counters_InvertedRate);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_InvertedRate %console_report "
+ "bar=%hrfloats foo=%hrfloats$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_InvertedRate\",$"},
+ {"\"family_index\": 4,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_InvertedRate\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_InvertedRate\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckInvertedRate(Results const& e) {
+ double t = e.DurationCPUTime(); // this (and not real time) is the time used
+ // check that the values are within 0.1% of the expected values
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, t / 8192.0, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_InvertedRate", &CheckInvertedRate);
+
+// ========================================================================= //
+// ------------------------- Thread Counters Output ------------------------ //
+// ========================================================================= //
+
+void BM_Counters_Threads(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ state.counters["foo"] = 1;
+ state.counters["bar"] = 2;
+}
+BENCHMARK(BM_Counters_Threads)->ThreadRange(1, 8);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_Threads/threads:%int %console_report "
+ "bar=%hrfloat foo=%hrfloat$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Threads/threads:%int\",$"},
+ {"\"family_index\": 5,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Threads/threads:%int\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(
+ TC_CSVOut,
+ {{"^\"BM_Counters_Threads/threads:%int\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckThreads(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "foo", EQ, e.NumThreads());
+ CHECK_COUNTER_VALUE(e, int, "bar", EQ, 2 * e.NumThreads());
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_Threads/threads:%int", &CheckThreads);
+
+// ========================================================================= //
+// ---------------------- ThreadAvg Counters Output ------------------------ //
+// ========================================================================= //
+
+void BM_Counters_AvgThreads(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kAvgThreads};
+ state.counters["bar"] = bm::Counter{2, bm::Counter::kAvgThreads};
+}
+BENCHMARK(BM_Counters_AvgThreads)->ThreadRange(1, 8);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_AvgThreads/threads:%int "
+ "%console_report bar=%hrfloat foo=%hrfloat$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_AvgThreads/threads:%int\",$"},
+ {"\"family_index\": 6,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_AvgThreads/threads:%int\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(
+ TC_CSVOut,
+ {{"^\"BM_Counters_AvgThreads/threads:%int\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckAvgThreads(Results const& e) {
+ CHECK_COUNTER_VALUE(e, int, "foo", EQ, 1);
+ CHECK_COUNTER_VALUE(e, int, "bar", EQ, 2);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_AvgThreads/threads:%int",
+ &CheckAvgThreads);
+
+// ========================================================================= //
+// ---------------------- ThreadAvg Counters Output ------------------------ //
+// ========================================================================= //
+
+void BM_Counters_AvgThreadsRate(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kAvgThreadsRate};
+ state.counters["bar"] = bm::Counter{2, bm::Counter::kAvgThreadsRate};
+}
+BENCHMARK(BM_Counters_AvgThreadsRate)->ThreadRange(1, 8);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_AvgThreadsRate/threads:%int "
+ "%console_report bar=%hrfloat/s foo=%hrfloat/s$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_AvgThreadsRate/threads:%int\",$"},
+ {"\"family_index\": 7,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_AvgThreadsRate/threads:%int\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_AvgThreadsRate/"
+ "threads:%int\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckAvgThreadsRate(Results const& e) {
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 1. / e.DurationCPUTime(), 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. / e.DurationCPUTime(), 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_AvgThreadsRate/threads:%int",
+ &CheckAvgThreadsRate);
+
+// ========================================================================= //
+// ------------------- IterationInvariant Counters Output ------------------ //
+// ========================================================================= //
+
+void BM_Counters_IterationInvariant(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kIsIterationInvariant};
+ state.counters["bar"] = bm::Counter{2, bm::Counter::kIsIterationInvariant};
+}
+BENCHMARK(BM_Counters_IterationInvariant);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_IterationInvariant %console_report "
+ "bar=%hrfloat foo=%hrfloat$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_IterationInvariant\",$"},
+ {"\"family_index\": 8,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_IterationInvariant\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_IterationInvariant\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckIterationInvariant(Results const& e) {
+ double its = e.NumIterations();
+ // check that the values are within 0.1% of the expected value
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, its, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. * its, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_IterationInvariant",
+ &CheckIterationInvariant);
+
+// ========================================================================= //
+// ----------------- IterationInvariantRate Counters Output ---------------- //
+// ========================================================================= //
+
+void BM_Counters_kIsIterationInvariantRate(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] =
+ bm::Counter{1, bm::Counter::kIsIterationInvariantRate};
+ state.counters["bar"] =
+ bm::Counter{2, bm::Counter::kIsRate | bm::Counter::kIsIterationInvariant};
+}
+BENCHMARK(BM_Counters_kIsIterationInvariantRate);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_kIsIterationInvariantRate "
+ "%console_report bar=%hrfloat/s foo=%hrfloat/s$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_kIsIterationInvariantRate\",$"},
+ {"\"family_index\": 9,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_kIsIterationInvariantRate\",$",
+ MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_kIsIterationInvariantRate\",%csv_report,"
+ "%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckIsIterationInvariantRate(Results const& e) {
+ double its = e.NumIterations();
+ double t = e.DurationCPUTime(); // this (and not real time) is the time used
+ // check that the values are within 0.1% of the expected values
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, its * 1. / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, its * 2. / t, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_kIsIterationInvariantRate",
+ &CheckIsIterationInvariantRate);
+
+// ========================================================================= //
+// ------------------- AvgIterations Counters Output ------------------ //
+// ========================================================================= //
+
+void BM_Counters_AvgIterations(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kAvgIterations};
+ state.counters["bar"] = bm::Counter{2, bm::Counter::kAvgIterations};
+}
+BENCHMARK(BM_Counters_AvgIterations);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_AvgIterations %console_report "
+ "bar=%hrfloat foo=%hrfloat$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_AvgIterations\",$"},
+ {"\"family_index\": 10,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_AvgIterations\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut,
+ {{"^\"BM_Counters_AvgIterations\",%csv_report,%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckAvgIterations(Results const& e) {
+ double its = e.NumIterations();
+ // check that the values are within 0.1% of the expected value
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 1. / its, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. / its, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_AvgIterations", &CheckAvgIterations);
+
+// ========================================================================= //
+// ----------------- AvgIterationsRate Counters Output ---------------- //
+// ========================================================================= //
+
+void BM_Counters_kAvgIterationsRate(benchmark::State& state) {
+ for (auto _ : state) {
+ // This test requires a non-zero CPU time to avoid divide-by-zero
+ benchmark::DoNotOptimize(state.iterations());
+ }
+ namespace bm = benchmark;
+ state.counters["foo"] = bm::Counter{1, bm::Counter::kAvgIterationsRate};
+ state.counters["bar"] =
+ bm::Counter{2, bm::Counter::kIsRate | bm::Counter::kAvgIterations};
+}
+BENCHMARK(BM_Counters_kAvgIterationsRate);
+ADD_CASES(TC_ConsoleOut, {{"^BM_Counters_kAvgIterationsRate "
+ "%console_report bar=%hrfloat/s foo=%hrfloat/s$"}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_kAvgIterationsRate\",$"},
+ {"\"family_index\": 11,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_kAvgIterationsRate\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 1,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"bar\": %float,$", MR_Next},
+ {"\"foo\": %float$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_CSVOut, {{"^\"BM_Counters_kAvgIterationsRate\",%csv_report,"
+ "%float,%float$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckAvgIterationsRate(Results const& e) {
+ double its = e.NumIterations();
+ double t = e.DurationCPUTime(); // this (and not real time) is the time used
+ // check that the values are within 0.1% of the expected values
+ CHECK_FLOAT_COUNTER_VALUE(e, "foo", EQ, 1. / its / t, 0.001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "bar", EQ, 2. / its / t, 0.001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_kAvgIterationsRate",
+ &CheckAvgIterationsRate);
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+
+#undef NDEBUG
+
+#include "benchmark/benchmark.h"
+#include "output_test.h"
+
+// ========================================================================= //
+// ------------------------ Thousands Customisation ------------------------ //
+// ========================================================================= //
+
+void BM_Counters_Thousands(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+ namespace bm = benchmark;
+ state.counters.insert({
+ {"t0_1000000DefaultBase",
+ bm::Counter(1000 * 1000, bm::Counter::kDefaults)},
+ {"t1_1000000Base1000", bm::Counter(1000 * 1000, bm::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000)},
+ {"t2_1000000Base1024", bm::Counter(1000 * 1000, bm::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1024)},
+ {"t3_1048576Base1000", bm::Counter(1024 * 1024, bm::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1000)},
+ {"t4_1048576Base1024", bm::Counter(1024 * 1024, bm::Counter::kDefaults,
+ benchmark::Counter::OneK::kIs1024)},
+ });
+}
+BENCHMARK(BM_Counters_Thousands)->Repetitions(2);
+ADD_CASES(
+ TC_ConsoleOut,
+ {
+ {"^BM_Counters_Thousands/repeats:2 %console_report "
+ "t0_1000000DefaultBase=1000k "
+ "t1_1000000Base1000=1000k t2_1000000Base1024=976.56[23]k "
+ "t3_1048576Base1000=1048.58k t4_1048576Base1024=1024k$"},
+ {"^BM_Counters_Thousands/repeats:2 %console_report "
+ "t0_1000000DefaultBase=1000k "
+ "t1_1000000Base1000=1000k t2_1000000Base1024=976.56[23]k "
+ "t3_1048576Base1000=1048.58k t4_1048576Base1024=1024k$"},
+ {"^BM_Counters_Thousands/repeats:2_mean %console_report "
+ "t0_1000000DefaultBase=1000k t1_1000000Base1000=1000k "
+ "t2_1000000Base1024=976.56[23]k t3_1048576Base1000=1048.58k "
+ "t4_1048576Base1024=1024k$"},
+ {"^BM_Counters_Thousands/repeats:2_median %console_report "
+ "t0_1000000DefaultBase=1000k t1_1000000Base1000=1000k "
+ "t2_1000000Base1024=976.56[23]k t3_1048576Base1000=1048.58k "
+ "t4_1048576Base1024=1024k$"},
+ {"^BM_Counters_Thousands/repeats:2_stddev %console_time_only_report [ "
+ "]*2 t0_1000000DefaultBase=0 t1_1000000Base1000=0 "
+ "t2_1000000Base1024=0 t3_1048576Base1000=0 t4_1048576Base1024=0$"},
+ });
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Thousands/repeats:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Thousands/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 0,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"t0_1000000DefaultBase\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t1_1000000Base1000\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t2_1000000Base1024\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t3_1048576Base1000\": 1\\.048576(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t4_1048576Base1024\": 1\\.048576(0)*e\\+(0)*6$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Thousands/repeats:2\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Thousands/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"iteration\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"repetition_index\": 1,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"iterations\": %int,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"t0_1000000DefaultBase\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t1_1000000Base1000\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t2_1000000Base1024\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t3_1048576Base1000\": 1\\.048576(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t4_1048576Base1024\": 1\\.048576(0)*e\\+(0)*6$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Thousands/repeats:2_mean\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Thousands/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"mean\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"t0_1000000DefaultBase\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t1_1000000Base1000\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t2_1000000Base1024\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t3_1048576Base1000\": 1\\.048576(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t4_1048576Base1024\": 1\\.048576(0)*e\\+(0)*6$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Thousands/repeats:2_median\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Thousands/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"median\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"t0_1000000DefaultBase\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t1_1000000Base1000\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t2_1000000Base1024\": 1\\.(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t3_1048576Base1000\": 1\\.048576(0)*e\\+(0)*6,$", MR_Next},
+ {"\"t4_1048576Base1024\": 1\\.048576(0)*e\\+(0)*6$", MR_Next},
+ {"}", MR_Next}});
+ADD_CASES(TC_JSONOut,
+ {{"\"name\": \"BM_Counters_Thousands/repeats:2_stddev\",$"},
+ {"\"family_index\": 0,$", MR_Next},
+ {"\"per_family_instance_index\": 0,$", MR_Next},
+ {"\"run_name\": \"BM_Counters_Thousands/repeats:2\",$", MR_Next},
+ {"\"run_type\": \"aggregate\",$", MR_Next},
+ {"\"repetitions\": 2,$", MR_Next},
+ {"\"threads\": 1,$", MR_Next},
+ {"\"aggregate_name\": \"stddev\",$", MR_Next},
+ {"\"iterations\": 2,$", MR_Next},
+ {"\"real_time\": %float,$", MR_Next},
+ {"\"cpu_time\": %float,$", MR_Next},
+ {"\"time_unit\": \"ns\",$", MR_Next},
+ {"\"t0_1000000DefaultBase\": 0\\.(0)*e\\+(0)*,$", MR_Next},
+ {"\"t1_1000000Base1000\": 0\\.(0)*e\\+(0)*,$", MR_Next},
+ {"\"t2_1000000Base1024\": 0\\.(0)*e\\+(0)*,$", MR_Next},
+ {"\"t3_1048576Base1000\": 0\\.(0)*e\\+(0)*,$", MR_Next},
+ {"\"t4_1048576Base1024\": 0\\.(0)*e\\+(0)*$", MR_Next},
+ {"}", MR_Next}});
+
+ADD_CASES(
+ TC_CSVOut,
+ {{"^\"BM_Counters_Thousands/"
+ "repeats:2\",%csv_report,1e\\+(0)*6,1e\\+(0)*6,1e\\+(0)*6,1\\.04858e\\+("
+ "0)*6,1\\.04858e\\+(0)*6$"},
+ {"^\"BM_Counters_Thousands/"
+ "repeats:2\",%csv_report,1e\\+(0)*6,1e\\+(0)*6,1e\\+(0)*6,1\\.04858e\\+("
+ "0)*6,1\\.04858e\\+(0)*6$"},
+ {"^\"BM_Counters_Thousands/"
+ "repeats:2_mean\",%csv_report,1e\\+(0)*6,1e\\+(0)*6,1e\\+(0)*6,1\\."
+ "04858e\\+(0)*6,1\\.04858e\\+(0)*6$"},
+ {"^\"BM_Counters_Thousands/"
+ "repeats:2_median\",%csv_report,1e\\+(0)*6,1e\\+(0)*6,1e\\+(0)*6,1\\."
+ "04858e\\+(0)*6,1\\.04858e\\+(0)*6$"},
+ {"^\"BM_Counters_Thousands/repeats:2_stddev\",%csv_report,0,0,0,0,0$"}});
+// VS2013 does not allow this function to be passed as a lambda argument
+// to CHECK_BENCHMARK_RESULTS()
+void CheckThousands(Results const& e) {
+ if (e.name != "BM_Counters_Thousands/repeats:2")
+ return; // Do not check the aggregates!
+
+ // check that the values are within 0.01% of the expected values
+ CHECK_FLOAT_COUNTER_VALUE(e, "t0_1000000DefaultBase", EQ, 1000 * 1000,
+ 0.0001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "t1_1000000Base1000", EQ, 1000 * 1000, 0.0001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "t2_1000000Base1024", EQ, 1000 * 1000, 0.0001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "t3_1048576Base1000", EQ, 1024 * 1024, 0.0001);
+ CHECK_FLOAT_COUNTER_VALUE(e, "t4_1048576Base1024", EQ, 1024 * 1024, 0.0001);
+}
+CHECK_BENCHMARK_RESULTS("BM_Counters_Thousands", &CheckThousands);
+
+// ========================================================================= //
+// --------------------------- TEST CASES END ------------------------------ //
+// ========================================================================= //
+
+int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
--- /dev/null
+load("@py_deps//:requirements.bzl", "requirement")
+
+py_library(
+ name = "gbench",
+ srcs = glob(["gbench/*.py"]),
+ deps = [
+ requirement("numpy"),
+ requirement("scipy"),
+ ],
+)
+
+py_binary(
+ name = "compare",
+ srcs = ["compare.py"],
+ python_version = "PY2",
+ deps = [
+ ":gbench",
+ ],
+)
--- /dev/null
+#!/usr/bin/env python
+
+import unittest
+"""
+compare.py - versatile benchmark output compare tool
+"""
+
+import argparse
+from argparse import ArgumentParser
+import json
+import sys
+import gbench
+from gbench import util, report
+from gbench.util import *
+
+
+def check_inputs(in1, in2, flags):
+ """
+ Perform checking on the user provided inputs and diagnose any abnormalities
+ """
+ in1_kind, in1_err = classify_input_file(in1)
+ in2_kind, in2_err = classify_input_file(in2)
+ output_file = find_benchmark_flag('--benchmark_out=', flags)
+ output_type = find_benchmark_flag('--benchmark_out_format=', flags)
+ if in1_kind == IT_Executable and in2_kind == IT_Executable and output_file:
+ print(("WARNING: '--benchmark_out=%s' will be passed to both "
+ "benchmarks causing it to be overwritten") % output_file)
+ if in1_kind == IT_JSON and in2_kind == IT_JSON and len(flags) > 0:
+ print("WARNING: passing optional flags has no effect since both "
+ "inputs are JSON")
+ if output_type is not None and output_type != 'json':
+ print(("ERROR: passing '--benchmark_out_format=%s' to 'compare.py`"
+ " is not supported.") % output_type)
+ sys.exit(1)
+
+
+def create_parser():
+ parser = ArgumentParser(
+ description='versatile benchmark output compare tool')
+
+ parser.add_argument(
+ '-a',
+ '--display_aggregates_only',
+ dest='display_aggregates_only',
+ action="store_true",
+ help="If there are repetitions, by default, we display everything - the"
+ " actual runs, and the aggregates computed. Sometimes, it is "
+ "desirable to only view the aggregates. E.g. when there are a lot "
+ "of repetitions. Do note that only the display is affected. "
+ "Internally, all the actual runs are still used, e.g. for U test.")
+
+ parser.add_argument(
+ '--no-color',
+ dest='color',
+ default=True,
+ action="store_false",
+ help="Do not use colors in the terminal output"
+ )
+
+ parser.add_argument(
+ '-d',
+ '--dump_to_json',
+ dest='dump_to_json',
+ help="Additionally, dump benchmark comparison output to this file in JSON format.")
+
+ utest = parser.add_argument_group()
+ utest.add_argument(
+ '--no-utest',
+ dest='utest',
+ default=True,
+ action="store_false",
+ help="The tool can do a two-tailed Mann-Whitney U test with the null hypothesis that it is equally likely that a randomly selected value from one sample will be less than or greater than a randomly selected value from a second sample.\nWARNING: requires **LARGE** (no less than {}) number of repetitions to be meaningful!\nThe test is being done by default, if at least {} repetitions were done.\nThis option can disable the U Test.".format(report.UTEST_OPTIMAL_REPETITIONS, report.UTEST_MIN_REPETITIONS))
+ alpha_default = 0.05
+ utest.add_argument(
+ "--alpha",
+ dest='utest_alpha',
+ default=alpha_default,
+ type=float,
+ help=("significance level alpha. if the calculated p-value is below this value, then the result is said to be statistically significant and the null hypothesis is rejected.\n(default: %0.4f)") %
+ alpha_default)
+
+ subparsers = parser.add_subparsers(
+ help='This tool has multiple modes of operation:',
+ dest='mode')
+
+ parser_a = subparsers.add_parser(
+ 'benchmarks',
+ help='The most simple use-case, compare all the output of these two benchmarks')
+ baseline = parser_a.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test_baseline',
+ metavar='test_baseline',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ contender = parser_a.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'test_contender',
+ metavar='test_contender',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ parser_a.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ parser_b = subparsers.add_parser(
+ 'filters', help='Compare filter one with the filter two of benchmark')
+ baseline = parser_b.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test',
+ metavar='test',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ baseline.add_argument(
+ 'filter_baseline',
+ metavar='filter_baseline',
+ type=str,
+ nargs=1,
+ help='The first filter, that will be used as baseline')
+ contender = parser_b.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'filter_contender',
+ metavar='filter_contender',
+ type=str,
+ nargs=1,
+ help='The second filter, that will be compared against the baseline')
+ parser_b.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ parser_c = subparsers.add_parser(
+ 'benchmarksfiltered',
+ help='Compare filter one of first benchmark with filter two of the second benchmark')
+ baseline = parser_c.add_argument_group(
+ 'baseline', 'The benchmark baseline')
+ baseline.add_argument(
+ 'test_baseline',
+ metavar='test_baseline',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='A benchmark executable or JSON output file')
+ baseline.add_argument(
+ 'filter_baseline',
+ metavar='filter_baseline',
+ type=str,
+ nargs=1,
+ help='The first filter, that will be used as baseline')
+ contender = parser_c.add_argument_group(
+ 'contender', 'The benchmark that will be compared against the baseline')
+ contender.add_argument(
+ 'test_contender',
+ metavar='test_contender',
+ type=argparse.FileType('r'),
+ nargs=1,
+ help='The second benchmark executable or JSON output file, that will be compared against the baseline')
+ contender.add_argument(
+ 'filter_contender',
+ metavar='filter_contender',
+ type=str,
+ nargs=1,
+ help='The second filter, that will be compared against the baseline')
+ parser_c.add_argument(
+ 'benchmark_options',
+ metavar='benchmark_options',
+ nargs=argparse.REMAINDER,
+ help='Arguments to pass when running benchmark executables')
+
+ return parser
+
+
+def main():
+ # Parse the command line flags
+ parser = create_parser()
+ args, unknown_args = parser.parse_known_args()
+ if args.mode is None:
+ parser.print_help()
+ exit(1)
+ assert not unknown_args
+ benchmark_options = args.benchmark_options
+
+ if args.mode == 'benchmarks':
+ test_baseline = args.test_baseline[0].name
+ test_contender = args.test_contender[0].name
+ filter_baseline = ''
+ filter_contender = ''
+
+ # NOTE: if test_baseline == test_contender, you are analyzing the stdev
+
+ description = 'Comparing %s to %s' % (test_baseline, test_contender)
+ elif args.mode == 'filters':
+ test_baseline = args.test[0].name
+ test_contender = args.test[0].name
+ filter_baseline = args.filter_baseline[0]
+ filter_contender = args.filter_contender[0]
+
+ # NOTE: if filter_baseline == filter_contender, you are analyzing the
+ # stdev
+
+ description = 'Comparing %s to %s (from %s)' % (
+ filter_baseline, filter_contender, args.test[0].name)
+ elif args.mode == 'benchmarksfiltered':
+ test_baseline = args.test_baseline[0].name
+ test_contender = args.test_contender[0].name
+ filter_baseline = args.filter_baseline[0]
+ filter_contender = args.filter_contender[0]
+
+ # NOTE: if test_baseline == test_contender and
+ # filter_baseline == filter_contender, you are analyzing the stdev
+
+ description = 'Comparing %s (from %s) to %s (from %s)' % (
+ filter_baseline, test_baseline, filter_contender, test_contender)
+ else:
+ # should never happen
+ print("Unrecognized mode of operation: '%s'" % args.mode)
+ parser.print_help()
+ exit(1)
+
+ check_inputs(test_baseline, test_contender, benchmark_options)
+
+ if args.display_aggregates_only:
+ benchmark_options += ['--benchmark_display_aggregates_only=true']
+
+ options_baseline = []
+ options_contender = []
+
+ if filter_baseline and filter_contender:
+ options_baseline = ['--benchmark_filter=%s' % filter_baseline]
+ options_contender = ['--benchmark_filter=%s' % filter_contender]
+
+ # Run the benchmarks and report the results
+ json1 = json1_orig = gbench.util.sort_benchmark_results(gbench.util.run_or_load_benchmark(
+ test_baseline, benchmark_options + options_baseline))
+ json2 = json2_orig = gbench.util.sort_benchmark_results(gbench.util.run_or_load_benchmark(
+ test_contender, benchmark_options + options_contender))
+
+ # Now, filter the benchmarks so that the difference report can work
+ if filter_baseline and filter_contender:
+ replacement = '[%s vs. %s]' % (filter_baseline, filter_contender)
+ json1 = gbench.report.filter_benchmark(
+ json1_orig, filter_baseline, replacement)
+ json2 = gbench.report.filter_benchmark(
+ json2_orig, filter_contender, replacement)
+
+ diff_report = gbench.report.get_difference_report(
+ json1, json2, args.utest)
+ output_lines = gbench.report.print_difference_report(
+ diff_report,
+ args.display_aggregates_only,
+ args.utest, args.utest_alpha, args.color)
+ print(description)
+ for ln in output_lines:
+ print(ln)
+
+ # Optionally, diff and output to JSON
+ if args.dump_to_json is not None:
+ with open(args.dump_to_json, 'w') as f_json:
+ json.dump(diff_report, f_json)
+
+class TestParser(unittest.TestCase):
+ def setUp(self):
+ self.parser = create_parser()
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'gbench',
+ 'Inputs')
+ self.testInput0 = os.path.join(testInputs, 'test1_run1.json')
+ self.testInput1 = os.path.join(testInputs, 'test1_run2.json')
+
+ def test_benchmarks_basic(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_without_utest(self):
+ parsed = self.parser.parse_args(
+ ['--no-utest', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertFalse(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.05)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_display_aggregates_only(self):
+ parsed = self.parser.parse_args(
+ ['-a', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertTrue(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_with_utest_alpha(self):
+ parsed = self.parser.parse_args(
+ ['--alpha=0.314', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.314)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_basic_without_utest_with_utest_alpha(self):
+ parsed = self.parser.parse_args(
+ ['--no-utest', '--alpha=0.314', 'benchmarks', self.testInput0, self.testInput1])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertFalse(parsed.utest)
+ self.assertEqual(parsed.utest_alpha, 0.314)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarks_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1, 'd'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.benchmark_options, ['d'])
+
+ def test_benchmarks_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['benchmarks', self.testInput0, self.testInput1, '--', 'e'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarks')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.benchmark_options, ['e'])
+
+ def test_filters_basic(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_filters_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd', 'e'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertEqual(parsed.benchmark_options, ['e'])
+
+ def test_filters_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['filters', self.testInput0, 'c', 'd', '--', 'f'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'filters')
+ self.assertEqual(parsed.test[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.filter_contender[0], 'd')
+ self.assertEqual(parsed.benchmark_options, ['f'])
+
+ def test_benchmarksfiltered_basic(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertFalse(parsed.benchmark_options)
+
+ def test_benchmarksfiltered_with_remainder(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e', 'f'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertEqual(parsed.benchmark_options[0], 'f')
+
+ def test_benchmarksfiltered_with_remainder_after_doubleminus(self):
+ parsed = self.parser.parse_args(
+ ['benchmarksfiltered', self.testInput0, 'c', self.testInput1, 'e', '--', 'g'])
+ self.assertFalse(parsed.display_aggregates_only)
+ self.assertTrue(parsed.utest)
+ self.assertEqual(parsed.mode, 'benchmarksfiltered')
+ self.assertEqual(parsed.test_baseline[0].name, self.testInput0)
+ self.assertEqual(parsed.filter_baseline[0], 'c')
+ self.assertEqual(parsed.test_contender[0].name, self.testInput1)
+ self.assertEqual(parsed.filter_contender[0], 'e')
+ self.assertEqual(parsed.benchmark_options[0], 'g')
+
+
+if __name__ == '__main__':
+ # unittest.main()
+ main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;
--- /dev/null
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_SameTimes",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_2xFaster",
+ "iterations": 1000,
+ "real_time": 50,
+ "cpu_time": 50,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_2xSlower",
+ "iterations": 1000,
+ "real_time": 50,
+ "cpu_time": 50,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentSlower",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentSlower",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_100xSlower",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_100xFaster",
+ "iterations": 1000,
+ "real_time": 10000,
+ "cpu_time": 10000,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentCPUToTime",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_ThirdFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "MyComplexityTest_BigO",
+ "run_name": "MyComplexityTest",
+ "run_type": "aggregate",
+ "aggregate_name": "BigO",
+ "cpu_coefficient": 4.2749856294592886e+00,
+ "real_coefficient": 6.4789275289789780e+00,
+ "big_o": "N",
+ "time_unit": "ns"
+ },
+ {
+ "name": "MyComplexityTest_RMS",
+ "run_name": "MyComplexityTest",
+ "run_type": "aggregate",
+ "aggregate_name": "RMS",
+ "rms": 4.5097802512472874e-03
+ },
+ {
+ "name": "BM_NotBadTimeUnit",
+ "iterations": 1000,
+ "real_time": 0.4,
+ "cpu_time": 0.5,
+ "time_unit": "s"
+ },
+ {
+ "name": "BM_DifferentTimeUnit",
+ "iterations": 1,
+ "real_time": 1,
+ "cpu_time": 1,
+ "time_unit": "s"
+ }
+ ]
+}
--- /dev/null
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_SameTimes",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_2xFaster",
+ "iterations": 1000,
+ "real_time": 25,
+ "cpu_time": 25,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_2xSlower",
+ "iterations": 20833333,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentFaster",
+ "iterations": 1000,
+ "real_time": 98.9999999,
+ "cpu_time": 98.9999999,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_1PercentSlower",
+ "iterations": 1000,
+ "real_time": 100.9999999,
+ "cpu_time": 100.9999999,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentFaster",
+ "iterations": 1000,
+ "real_time": 90,
+ "cpu_time": 90,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentSlower",
+ "iterations": 1000,
+ "real_time": 110,
+ "cpu_time": 110,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_100xSlower",
+ "iterations": 1000,
+ "real_time": 1.0000e+04,
+ "cpu_time": 1.0000e+04,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_100xFaster",
+ "iterations": 1000,
+ "real_time": 100,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_10PercentCPUToTime",
+ "iterations": 1000,
+ "real_time": 110,
+ "cpu_time": 90,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_ThirdFaster",
+ "iterations": 1000,
+ "real_time": 66.665,
+ "cpu_time": 66.664,
+ "time_unit": "ns"
+ },
+ {
+ "name": "MyComplexityTest_BigO",
+ "run_name": "MyComplexityTest",
+ "run_type": "aggregate",
+ "aggregate_name": "BigO",
+ "cpu_coefficient": 5.6215779594361486e+00,
+ "real_coefficient": 5.6288314793554610e+00,
+ "big_o": "N",
+ "time_unit": "ns"
+ },
+ {
+ "name": "MyComplexityTest_RMS",
+ "run_name": "MyComplexityTest",
+ "run_type": "aggregate",
+ "aggregate_name": "RMS",
+ "rms": 3.3128901852342174e-03
+ },
+ {
+ "name": "BM_NotBadTimeUnit",
+ "iterations": 1000,
+ "real_time": 0.04,
+ "cpu_time": 0.6,
+ "time_unit": "s"
+ },
+ {
+ "name": "BM_DifferentTimeUnit",
+ "iterations": 1,
+ "real_time": 1,
+ "cpu_time": 1,
+ "time_unit": "ns"
+ }
+ ]
+}
--- /dev/null
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_Hi",
+ "iterations": 1234,
+ "real_time": 42,
+ "cpu_time": 24,
+ "time_unit": "ms"
+ },
+ {
+ "name": "BM_Zero",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Zero/4",
+ "iterations": 4000,
+ "real_time": 40,
+ "cpu_time": 40,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_Zero",
+ "iterations": 2000,
+ "real_time": 20,
+ "cpu_time": 20,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_Zero/3",
+ "iterations": 3000,
+ "real_time": 30,
+ "cpu_time": 30,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_One",
+ "iterations": 5000,
+ "real_time": 5,
+ "cpu_time": 5,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_One/4",
+ "iterations": 2000,
+ "real_time": 20,
+ "cpu_time": 20,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_One",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 10,
+ "time_unit": "ns"
+ },
+ {
+ "name": "Prefix/BM_One/3",
+ "iterations": 1500,
+ "real_time": 15,
+ "cpu_time": 15,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Bye",
+ "iterations": 5321,
+ "real_time": 11,
+ "cpu_time": 63,
+ "time_unit": "ns"
+ }
+ ]
+}
--- /dev/null
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_One",
+ "run_type": "aggregate",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 100,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 9,
+ "cpu_time": 90,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 86,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "run_type": "aggregate",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 80,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "run_type": "aggregate",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 77,
+ "time_unit": "ns"
+ },
+ {
+ "name": "medium",
+ "run_type": "iteration",
+ "iterations": 1000,
+ "real_time": 8,
+ "cpu_time": 80,
+ "time_unit": "ns"
+ },
+ {
+ "name": "medium",
+ "run_type": "iteration",
+ "iterations": 1000,
+ "real_time": 9,
+ "cpu_time": 82,
+ "time_unit": "ns"
+ }
+ ]
+}
--- /dev/null
+{
+ "context": {
+ "date": "2016-08-02 17:44:46",
+ "num_cpus": 4,
+ "mhz_per_cpu": 4228,
+ "cpu_scaling_enabled": false,
+ "library_build_type": "release"
+ },
+ "benchmarks": [
+ {
+ "name": "BM_One",
+ "iterations": 1000,
+ "real_time": 9,
+ "cpu_time": 110,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "run_type": "aggregate",
+ "iterations": 1000,
+ "real_time": 10,
+ "cpu_time": 89,
+ "time_unit": "ns"
+ },
+ {
+ "name": "BM_Two",
+ "iterations": 1000,
+ "real_time": 7,
+ "cpu_time": 72,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "run_type": "aggregate",
+ "iterations": 1000,
+ "real_time": 7,
+ "cpu_time": 75,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "run_type": "aggregate",
+ "iterations": 762,
+ "real_time": 4.54,
+ "cpu_time": 66.6,
+ "time_unit": "ns"
+ },
+ {
+ "name": "short",
+ "run_type": "iteration",
+ "iterations": 1000,
+ "real_time": 800,
+ "cpu_time": 1,
+ "time_unit": "ns"
+ },
+ {
+ "name": "medium",
+ "run_type": "iteration",
+ "iterations": 1200,
+ "real_time": 5,
+ "cpu_time": 53,
+ "time_unit": "ns"
+ }
+ ]
+}
--- /dev/null
+{
+ "benchmarks": [
+ {
+ "name": "99 family 0 instance 0 repetition 0",
+ "run_type": "iteration",
+ "family_index": 0,
+ "per_family_instance_index": 0,
+ "repetition_index": 0
+ },
+ {
+ "name": "98 family 0 instance 0 repetition 1",
+ "run_type": "iteration",
+ "family_index": 0,
+ "per_family_instance_index": 0,
+ "repetition_index": 1
+ },
+ {
+ "name": "97 family 0 instance 0 aggregate",
+ "run_type": "aggregate",
+ "family_index": 0,
+ "per_family_instance_index": 0,
+ "aggregate_name": "9 aggregate"
+ },
+
+
+ {
+ "name": "96 family 0 instance 1 repetition 0",
+ "run_type": "iteration",
+ "family_index": 0,
+ "per_family_instance_index": 1,
+ "repetition_index": 0
+ },
+ {
+ "name": "95 family 0 instance 1 repetition 1",
+ "run_type": "iteration",
+ "family_index": 0,
+ "per_family_instance_index": 1,
+ "repetition_index": 1
+ },
+ {
+ "name": "94 family 0 instance 1 aggregate",
+ "run_type": "aggregate",
+ "family_index": 0,
+ "per_family_instance_index": 1,
+ "aggregate_name": "9 aggregate"
+ },
+
+
+
+
+ {
+ "name": "93 family 1 instance 0 repetition 0",
+ "run_type": "iteration",
+ "family_index": 1,
+ "per_family_instance_index": 0,
+ "repetition_index": 0
+ },
+ {
+ "name": "92 family 1 instance 0 repetition 1",
+ "run_type": "iteration",
+ "family_index": 1,
+ "per_family_instance_index": 0,
+ "repetition_index": 1
+ },
+ {
+ "name": "91 family 1 instance 0 aggregate",
+ "run_type": "aggregate",
+ "family_index": 1,
+ "per_family_instance_index": 0,
+ "aggregate_name": "9 aggregate"
+ },
+
+
+ {
+ "name": "90 family 1 instance 1 repetition 0",
+ "run_type": "iteration",
+ "family_index": 1,
+ "per_family_instance_index": 1,
+ "repetition_index": 0
+ },
+ {
+ "name": "89 family 1 instance 1 repetition 1",
+ "run_type": "iteration",
+ "family_index": 1,
+ "per_family_instance_index": 1,
+ "repetition_index": 1
+ },
+ {
+ "name": "88 family 1 instance 1 aggregate",
+ "run_type": "aggregate",
+ "family_index": 1,
+ "per_family_instance_index": 1,
+ "aggregate_name": "9 aggregate"
+ }
+ ]
+}
--- /dev/null
+"""Google Benchmark tooling"""
+
+__author__ = 'Eric Fiselier'
+__email__ = 'eric@efcs.ca'
+__versioninfo__ = (0, 5, 0)
+__version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev'
+
+__all__ = []
--- /dev/null
+"""report.py - Utilities for reporting statistics about benchmark results
+"""
+
+import unittest
+import os
+import re
+import copy
+import random
+
+from scipy.stats import mannwhitneyu
+
+
+class BenchmarkColor(object):
+ def __init__(self, name, code):
+ self.name = name
+ self.code = code
+
+ def __repr__(self):
+ return '%s%r' % (self.__class__.__name__,
+ (self.name, self.code))
+
+ def __format__(self, format):
+ return self.code
+
+
+# Benchmark Colors Enumeration
+BC_NONE = BenchmarkColor('NONE', '')
+BC_MAGENTA = BenchmarkColor('MAGENTA', '\033[95m')
+BC_CYAN = BenchmarkColor('CYAN', '\033[96m')
+BC_OKBLUE = BenchmarkColor('OKBLUE', '\033[94m')
+BC_OKGREEN = BenchmarkColor('OKGREEN', '\033[32m')
+BC_HEADER = BenchmarkColor('HEADER', '\033[92m')
+BC_WARNING = BenchmarkColor('WARNING', '\033[93m')
+BC_WHITE = BenchmarkColor('WHITE', '\033[97m')
+BC_FAIL = BenchmarkColor('FAIL', '\033[91m')
+BC_ENDC = BenchmarkColor('ENDC', '\033[0m')
+BC_BOLD = BenchmarkColor('BOLD', '\033[1m')
+BC_UNDERLINE = BenchmarkColor('UNDERLINE', '\033[4m')
+
+UTEST_MIN_REPETITIONS = 2
+UTEST_OPTIMAL_REPETITIONS = 9 # Lowest reasonable number, More is better.
+UTEST_COL_NAME = "_pvalue"
+
+
+def color_format(use_color, fmt_str, *args, **kwargs):
+ """
+ Return the result of 'fmt_str.format(*args, **kwargs)' after transforming
+ 'args' and 'kwargs' according to the value of 'use_color'. If 'use_color'
+ is False then all color codes in 'args' and 'kwargs' are replaced with
+ the empty string.
+ """
+ assert use_color is True or use_color is False
+ if not use_color:
+ args = [arg if not isinstance(arg, BenchmarkColor) else BC_NONE
+ for arg in args]
+ kwargs = {key: arg if not isinstance(arg, BenchmarkColor) else BC_NONE
+ for key, arg in kwargs.items()}
+ return fmt_str.format(*args, **kwargs)
+
+
+def find_longest_name(benchmark_list):
+ """
+ Return the length of the longest benchmark name in a given list of
+ benchmark JSON objects
+ """
+ longest_name = 1
+ for bc in benchmark_list:
+ if len(bc['name']) > longest_name:
+ longest_name = len(bc['name'])
+ return longest_name
+
+
+def calculate_change(old_val, new_val):
+ """
+ Return a float representing the decimal change between old_val and new_val.
+ """
+ if old_val == 0 and new_val == 0:
+ return 0.0
+ if old_val == 0:
+ return float(new_val - old_val) / (float(old_val + new_val) / 2)
+ return float(new_val - old_val) / abs(old_val)
+
+
+def filter_benchmark(json_orig, family, replacement=""):
+ """
+ Apply a filter to the json, and only leave the 'family' of benchmarks.
+ """
+ regex = re.compile(family)
+ filtered = {}
+ filtered['benchmarks'] = []
+ for be in json_orig['benchmarks']:
+ if not regex.search(be['name']):
+ continue
+ filteredbench = copy.deepcopy(be) # Do NOT modify the old name!
+ filteredbench['name'] = regex.sub(replacement, filteredbench['name'])
+ filtered['benchmarks'].append(filteredbench)
+ return filtered
+
+
+def get_unique_benchmark_names(json):
+ """
+ While *keeping* the order, give all the unique 'names' used for benchmarks.
+ """
+ seen = set()
+ uniqued = [x['name'] for x in json['benchmarks']
+ if x['name'] not in seen and
+ (seen.add(x['name']) or True)]
+ return uniqued
+
+
+def intersect(list1, list2):
+ """
+ Given two lists, get a new list consisting of the elements only contained
+ in *both of the input lists*, while preserving the ordering.
+ """
+ return [x for x in list1 if x in list2]
+
+
+def is_potentially_comparable_benchmark(x):
+ return ('time_unit' in x and 'real_time' in x and 'cpu_time' in x)
+
+
+def partition_benchmarks(json1, json2):
+ """
+ While preserving the ordering, find benchmarks with the same names in
+ both of the inputs, and group them.
+ (i.e. partition/filter into groups with common name)
+ """
+ json1_unique_names = get_unique_benchmark_names(json1)
+ json2_unique_names = get_unique_benchmark_names(json2)
+ names = intersect(json1_unique_names, json2_unique_names)
+ partitions = []
+ for name in names:
+ time_unit = None
+ # Pick the time unit from the first entry of the lhs benchmark.
+ # We should be careful not to crash with unexpected input.
+ for x in json1['benchmarks']:
+ if (x['name'] == name and is_potentially_comparable_benchmark(x)):
+ time_unit = x['time_unit']
+ break
+ if time_unit is None:
+ continue
+ # Filter by name and time unit.
+ # All the repetitions are assumed to be comparable.
+ lhs = [x for x in json1['benchmarks'] if x['name'] == name and
+ x['time_unit'] == time_unit]
+ rhs = [x for x in json2['benchmarks'] if x['name'] == name and
+ x['time_unit'] == time_unit]
+ partitions.append([lhs, rhs])
+ return partitions
+
+
+def extract_field(partition, field_name):
+ # The count of elements may be different. We want *all* of them.
+ lhs = [x[field_name] for x in partition[0]]
+ rhs = [x[field_name] for x in partition[1]]
+ return [lhs, rhs]
+
+
+def calc_utest(timings_cpu, timings_time):
+ min_rep_cnt = min(len(timings_time[0]),
+ len(timings_time[1]),
+ len(timings_cpu[0]),
+ len(timings_cpu[1]))
+
+ # Does *everything* has at least UTEST_MIN_REPETITIONS repetitions?
+ if min_rep_cnt < UTEST_MIN_REPETITIONS:
+ return False, None, None
+
+ time_pvalue = mannwhitneyu(
+ timings_time[0], timings_time[1], alternative='two-sided').pvalue
+ cpu_pvalue = mannwhitneyu(
+ timings_cpu[0], timings_cpu[1], alternative='two-sided').pvalue
+
+ return (min_rep_cnt >= UTEST_OPTIMAL_REPETITIONS), cpu_pvalue, time_pvalue
+
+def print_utest(bc_name, utest, utest_alpha, first_col_width, use_color=True):
+ def get_utest_color(pval):
+ return BC_FAIL if pval >= utest_alpha else BC_OKGREEN
+
+ # Check if we failed miserably with minimum required repetitions for utest
+ if not utest['have_optimal_repetitions'] and utest['cpu_pvalue'] is None and utest['time_pvalue'] is None:
+ return []
+
+ dsc = "U Test, Repetitions: {} vs {}".format(
+ utest['nr_of_repetitions'], utest['nr_of_repetitions_other'])
+ dsc_color = BC_OKGREEN
+
+ # We still got some results to show but issue a warning about it.
+ if not utest['have_optimal_repetitions']:
+ dsc_color = BC_WARNING
+ dsc += ". WARNING: Results unreliable! {}+ repetitions recommended.".format(
+ UTEST_OPTIMAL_REPETITIONS)
+
+ special_str = "{}{:<{}s}{endc}{}{:16.4f}{endc}{}{:16.4f}{endc}{} {}"
+
+ return [color_format(use_color,
+ special_str,
+ BC_HEADER,
+ "{}{}".format(bc_name, UTEST_COL_NAME),
+ first_col_width,
+ get_utest_color(
+ utest['time_pvalue']), utest['time_pvalue'],
+ get_utest_color(
+ utest['cpu_pvalue']), utest['cpu_pvalue'],
+ dsc_color, dsc,
+ endc=BC_ENDC)]
+
+
+def get_difference_report(
+ json1,
+ json2,
+ utest=False):
+ """
+ Calculate and report the difference between each test of two benchmarks
+ runs specified as 'json1' and 'json2'. Output is another json containing
+ relevant details for each test run.
+ """
+ assert utest is True or utest is False
+
+ diff_report = []
+ partitions = partition_benchmarks(json1, json2)
+ for partition in partitions:
+ benchmark_name = partition[0][0]['name']
+ time_unit = partition[0][0]['time_unit']
+ measurements = []
+ utest_results = {}
+ # Careful, we may have different repetition count.
+ for i in range(min(len(partition[0]), len(partition[1]))):
+ bn = partition[0][i]
+ other_bench = partition[1][i]
+ measurements.append({
+ 'real_time': bn['real_time'],
+ 'cpu_time': bn['cpu_time'],
+ 'real_time_other': other_bench['real_time'],
+ 'cpu_time_other': other_bench['cpu_time'],
+ 'time': calculate_change(bn['real_time'], other_bench['real_time']),
+ 'cpu': calculate_change(bn['cpu_time'], other_bench['cpu_time'])
+ })
+
+ # After processing the whole partition, if requested, do the U test.
+ if utest:
+ timings_cpu = extract_field(partition, 'cpu_time')
+ timings_time = extract_field(partition, 'real_time')
+ have_optimal_repetitions, cpu_pvalue, time_pvalue = calc_utest(timings_cpu, timings_time)
+ if cpu_pvalue and time_pvalue:
+ utest_results = {
+ 'have_optimal_repetitions': have_optimal_repetitions,
+ 'cpu_pvalue': cpu_pvalue,
+ 'time_pvalue': time_pvalue,
+ 'nr_of_repetitions': len(timings_cpu[0]),
+ 'nr_of_repetitions_other': len(timings_cpu[1])
+ }
+
+ # Store only if we had any measurements for given benchmark.
+ # E.g. partition_benchmarks will filter out the benchmarks having
+ # time units which are not compatible with other time units in the
+ # benchmark suite.
+ if measurements:
+ run_type = partition[0][0]['run_type'] if 'run_type' in partition[0][0] else ''
+ aggregate_name = partition[0][0]['aggregate_name'] if run_type == 'aggregate' and 'aggregate_name' in partition[0][0] else ''
+ diff_report.append({
+ 'name': benchmark_name,
+ 'measurements': measurements,
+ 'time_unit': time_unit,
+ 'run_type': run_type,
+ 'aggregate_name': aggregate_name,
+ 'utest': utest_results
+ })
+
+ return diff_report
+
+
+def print_difference_report(
+ json_diff_report,
+ include_aggregates_only=False,
+ utest=False,
+ utest_alpha=0.05,
+ use_color=True):
+ """
+ Calculate and report the difference between each test of two benchmarks
+ runs specified as 'json1' and 'json2'.
+ """
+ assert utest is True or utest is False
+
+ def get_color(res):
+ if res > 0.05:
+ return BC_FAIL
+ elif res > -0.07:
+ return BC_WHITE
+ else:
+ return BC_CYAN
+
+ first_col_width = find_longest_name(json_diff_report)
+ first_col_width = max(
+ first_col_width,
+ len('Benchmark'))
+ first_col_width += len(UTEST_COL_NAME)
+ first_line = "{:<{}s}Time CPU Time Old Time New CPU Old CPU New".format(
+ 'Benchmark', 12 + first_col_width)
+ output_strs = [first_line, '-' * len(first_line)]
+
+ fmt_str = "{}{:<{}s}{endc}{}{:+16.4f}{endc}{}{:+16.4f}{endc}{:14.0f}{:14.0f}{endc}{:14.0f}{:14.0f}"
+ for benchmark in json_diff_report:
+ # *If* we were asked to only include aggregates,
+ # and if it is non-aggregate, then don't print it.
+ if not include_aggregates_only or not 'run_type' in benchmark or benchmark['run_type'] == 'aggregate':
+ for measurement in benchmark['measurements']:
+ output_strs += [color_format(use_color,
+ fmt_str,
+ BC_HEADER,
+ benchmark['name'],
+ first_col_width,
+ get_color(measurement['time']),
+ measurement['time'],
+ get_color(measurement['cpu']),
+ measurement['cpu'],
+ measurement['real_time'],
+ measurement['real_time_other'],
+ measurement['cpu_time'],
+ measurement['cpu_time_other'],
+ endc=BC_ENDC)]
+
+ # After processing the measurements, if requested and
+ # if applicable (e.g. u-test exists for given benchmark),
+ # print the U test.
+ if utest and benchmark['utest']:
+ output_strs += print_utest(benchmark['name'],
+ benchmark['utest'],
+ utest_alpha=utest_alpha,
+ first_col_width=first_col_width,
+ use_color=use_color)
+
+ return output_strs
+
+
+###############################################################################
+# Unit tests
+
+
+class TestGetUniqueBenchmarkNames(unittest.TestCase):
+ def load_results(self):
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput = os.path.join(testInputs, 'test3_run0.json')
+ with open(testOutput, 'r') as f:
+ json = json.load(f)
+ return json
+
+ def test_basic(self):
+ expect_lines = [
+ 'BM_One',
+ 'BM_Two',
+ 'short', # These two are not sorted
+ 'medium', # These two are not sorted
+ ]
+ json = self.load_results()
+ output_lines = get_unique_benchmark_names(json)
+ print("\n")
+ print("\n".join(output_lines))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ self.assertEqual(expect_lines[i], output_lines[i])
+
+
+class TestReportDifference(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ def load_results():
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput1 = os.path.join(testInputs, 'test1_run1.json')
+ testOutput2 = os.path.join(testInputs, 'test1_run2.json')
+ with open(testOutput1, 'r') as f:
+ json1 = json.load(f)
+ with open(testOutput2, 'r') as f:
+ json2 = json.load(f)
+ return json1, json2
+
+ json1, json2 = load_results()
+ cls.json_diff_report = get_difference_report(json1, json2)
+
+ def test_json_diff_report_pretty_printing(self):
+ expect_lines = [
+ ['BM_SameTimes', '+0.0000', '+0.0000', '10', '10', '10', '10'],
+ ['BM_2xFaster', '-0.5000', '-0.5000', '50', '25', '50', '25'],
+ ['BM_2xSlower', '+1.0000', '+1.0000', '50', '100', '50', '100'],
+ ['BM_1PercentFaster', '-0.0100', '-0.0100', '100', '99', '100', '99'],
+ ['BM_1PercentSlower', '+0.0100', '+0.0100', '100', '101', '100', '101'],
+ ['BM_10PercentFaster', '-0.1000', '-0.1000', '100', '90', '100', '90'],
+ ['BM_10PercentSlower', '+0.1000', '+0.1000', '100', '110', '100', '110'],
+ ['BM_100xSlower', '+99.0000', '+99.0000',
+ '100', '10000', '100', '10000'],
+ ['BM_100xFaster', '-0.9900', '-0.9900',
+ '10000', '100', '10000', '100'],
+ ['BM_10PercentCPUToTime', '+0.1000',
+ '-0.1000', '100', '110', '100', '90'],
+ ['BM_ThirdFaster', '-0.3333', '-0.3334', '100', '67', '100', '67'],
+ ['BM_NotBadTimeUnit', '-0.9000', '+0.2000', '0', '0', '0', '1'],
+ ]
+ output_lines_with_header = print_difference_report(
+ self.json_diff_report, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(len(parts), 7)
+ self.assertEqual(expect_lines[i], parts)
+
+ def test_json_diff_report_output(self):
+ expected_output = [
+ {
+ 'name': 'BM_SameTimes',
+ 'measurements': [{'time': 0.0000, 'cpu': 0.0000, 'real_time': 10, 'real_time_other': 10, 'cpu_time': 10, 'cpu_time_other': 10}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_2xFaster',
+ 'measurements': [{'time': -0.5000, 'cpu': -0.5000, 'real_time': 50, 'real_time_other': 25, 'cpu_time': 50, 'cpu_time_other': 25}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_2xSlower',
+ 'measurements': [{'time': 1.0000, 'cpu': 1.0000, 'real_time': 50, 'real_time_other': 100, 'cpu_time': 50, 'cpu_time_other': 100}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_1PercentFaster',
+ 'measurements': [{'time': -0.0100, 'cpu': -0.0100, 'real_time': 100, 'real_time_other': 98.9999999, 'cpu_time': 100, 'cpu_time_other': 98.9999999}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_1PercentSlower',
+ 'measurements': [{'time': 0.0100, 'cpu': 0.0100, 'real_time': 100, 'real_time_other': 101, 'cpu_time': 100, 'cpu_time_other': 101}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_10PercentFaster',
+ 'measurements': [{'time': -0.1000, 'cpu': -0.1000, 'real_time': 100, 'real_time_other': 90, 'cpu_time': 100, 'cpu_time_other': 90}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_10PercentSlower',
+ 'measurements': [{'time': 0.1000, 'cpu': 0.1000, 'real_time': 100, 'real_time_other': 110, 'cpu_time': 100, 'cpu_time_other': 110}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_100xSlower',
+ 'measurements': [{'time': 99.0000, 'cpu': 99.0000, 'real_time': 100, 'real_time_other': 10000, 'cpu_time': 100, 'cpu_time_other': 10000}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_100xFaster',
+ 'measurements': [{'time': -0.9900, 'cpu': -0.9900, 'real_time': 10000, 'real_time_other': 100, 'cpu_time': 10000, 'cpu_time_other': 100}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_10PercentCPUToTime',
+ 'measurements': [{'time': 0.1000, 'cpu': -0.1000, 'real_time': 100, 'real_time_other': 110, 'cpu_time': 100, 'cpu_time_other': 90}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_ThirdFaster',
+ 'measurements': [{'time': -0.3333, 'cpu': -0.3334, 'real_time': 100, 'real_time_other': 67, 'cpu_time': 100, 'cpu_time_other': 67}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': 'BM_NotBadTimeUnit',
+ 'measurements': [{'time': -0.9000, 'cpu': 0.2000, 'real_time': 0.4, 'real_time_other': 0.04, 'cpu_time': 0.5, 'cpu_time_other': 0.6}],
+ 'time_unit': 's',
+ 'utest': {}
+ },
+ ]
+ self.assertEqual(len(self.json_diff_report), len(expected_output))
+ for out, expected in zip(
+ self.json_diff_report, expected_output):
+ self.assertEqual(out['name'], expected['name'])
+ self.assertEqual(out['time_unit'], expected['time_unit'])
+ assert_utest(self, out, expected)
+ assert_measurements(self, out, expected)
+
+
+class TestReportDifferenceBetweenFamilies(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ def load_result():
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput = os.path.join(testInputs, 'test2_run.json')
+ with open(testOutput, 'r') as f:
+ json = json.load(f)
+ return json
+
+ json = load_result()
+ json1 = filter_benchmark(json, "BM_Z.ro", ".")
+ json2 = filter_benchmark(json, "BM_O.e", ".")
+ cls.json_diff_report = get_difference_report(json1, json2)
+
+ def test_json_diff_report_pretty_printing(self):
+ expect_lines = [
+ ['.', '-0.5000', '-0.5000', '10', '5', '10', '5'],
+ ['./4', '-0.5000', '-0.5000', '40', '20', '40', '20'],
+ ['Prefix/.', '-0.5000', '-0.5000', '20', '10', '20', '10'],
+ ['Prefix/./3', '-0.5000', '-0.5000', '30', '15', '30', '15'],
+ ]
+ output_lines_with_header = print_difference_report(
+ self.json_diff_report, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(len(parts), 7)
+ self.assertEqual(expect_lines[i], parts)
+
+ def test_json_diff_report(self):
+ expected_output = [
+ {
+ 'name': u'.',
+ 'measurements': [{'time': -0.5, 'cpu': -0.5, 'real_time': 10, 'real_time_other': 5, 'cpu_time': 10, 'cpu_time_other': 5}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': u'./4',
+ 'measurements': [{'time': -0.5, 'cpu': -0.5, 'real_time': 40, 'real_time_other': 20, 'cpu_time': 40, 'cpu_time_other': 20}],
+ 'time_unit': 'ns',
+ 'utest': {},
+ },
+ {
+ 'name': u'Prefix/.',
+ 'measurements': [{'time': -0.5, 'cpu': -0.5, 'real_time': 20, 'real_time_other': 10, 'cpu_time': 20, 'cpu_time_other': 10}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': u'Prefix/./3',
+ 'measurements': [{'time': -0.5, 'cpu': -0.5, 'real_time': 30, 'real_time_other': 15, 'cpu_time': 30, 'cpu_time_other': 15}],
+ 'time_unit': 'ns',
+ 'utest': {}
+ }
+ ]
+ self.assertEqual(len(self.json_diff_report), len(expected_output))
+ for out, expected in zip(
+ self.json_diff_report, expected_output):
+ self.assertEqual(out['name'], expected['name'])
+ self.assertEqual(out['time_unit'], expected['time_unit'])
+ assert_utest(self, out, expected)
+ assert_measurements(self, out, expected)
+
+
+class TestReportDifferenceWithUTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ def load_results():
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput1 = os.path.join(testInputs, 'test3_run0.json')
+ testOutput2 = os.path.join(testInputs, 'test3_run1.json')
+ with open(testOutput1, 'r') as f:
+ json1 = json.load(f)
+ with open(testOutput2, 'r') as f:
+ json2 = json.load(f)
+ return json1, json2
+
+ json1, json2 = load_results()
+ cls.json_diff_report = get_difference_report(
+ json1, json2, utest=True)
+
+ def test_json_diff_report_pretty_printing(self):
+ expect_lines = [
+ ['BM_One', '-0.1000', '+0.1000', '10', '9', '100', '110'],
+ ['BM_Two', '+0.1111', '-0.0111', '9', '10', '90', '89'],
+ ['BM_Two', '-0.1250', '-0.1628', '8', '7', '86', '72'],
+ ['BM_Two_pvalue',
+ '0.6985',
+ '0.6985',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '2.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['short', '-0.1250', '-0.0625', '8', '7', '80', '75'],
+ ['short', '-0.4325', '-0.1351', '8', '5', '77', '67'],
+ ['short_pvalue',
+ '0.7671',
+ '0.1489',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '3.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['medium', '-0.3750', '-0.3375', '8', '5', '80', '53'],
+ ]
+ output_lines_with_header = print_difference_report(
+ self.json_diff_report, utest=True, utest_alpha=0.05, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(expect_lines[i], parts)
+
+ def test_json_diff_report_pretty_printing_aggregates_only(self):
+ expect_lines = [
+ ['BM_One', '-0.1000', '+0.1000', '10', '9', '100', '110'],
+ ['BM_Two_pvalue',
+ '0.6985',
+ '0.6985',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '2.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['short', '-0.1250', '-0.0625', '8', '7', '80', '75'],
+ ['short', '-0.4325', '-0.1351', '8', '5', '77', '67'],
+ ['short_pvalue',
+ '0.7671',
+ '0.1489',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '3.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ]
+ output_lines_with_header = print_difference_report(
+ self.json_diff_report, include_aggregates_only=True, utest=True, utest_alpha=0.05, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(expect_lines[i], parts)
+
+ def test_json_diff_report(self):
+ expected_output = [
+ {
+ 'name': u'BM_One',
+ 'measurements': [
+ {'time': -0.1,
+ 'cpu': 0.1,
+ 'real_time': 10,
+ 'real_time_other': 9,
+ 'cpu_time': 100,
+ 'cpu_time_other': 110}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': u'BM_Two',
+ 'measurements': [
+ {'time': 0.1111111111111111,
+ 'cpu': -0.011111111111111112,
+ 'real_time': 9,
+ 'real_time_other': 10,
+ 'cpu_time': 90,
+ 'cpu_time_other': 89},
+ {'time': -0.125, 'cpu': -0.16279069767441862, 'real_time': 8,
+ 'real_time_other': 7, 'cpu_time': 86, 'cpu_time_other': 72}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {
+ 'have_optimal_repetitions': False, 'cpu_pvalue': 0.6985353583033387, 'time_pvalue': 0.6985353583033387
+ }
+ },
+ {
+ 'name': u'short',
+ 'measurements': [
+ {'time': -0.125,
+ 'cpu': -0.0625,
+ 'real_time': 8,
+ 'real_time_other': 7,
+ 'cpu_time': 80,
+ 'cpu_time_other': 75},
+ {'time': -0.4325,
+ 'cpu': -0.13506493506493514,
+ 'real_time': 8,
+ 'real_time_other': 4.54,
+ 'cpu_time': 77,
+ 'cpu_time_other': 66.6}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {
+ 'have_optimal_repetitions': False, 'cpu_pvalue': 0.14891467317876572, 'time_pvalue': 0.7670968684102772
+ }
+ },
+ {
+ 'name': u'medium',
+ 'measurements': [
+ {'time': -0.375,
+ 'cpu': -0.3375,
+ 'real_time': 8,
+ 'real_time_other': 5,
+ 'cpu_time': 80,
+ 'cpu_time_other': 53}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {}
+ }
+ ]
+ self.assertEqual(len(self.json_diff_report), len(expected_output))
+ for out, expected in zip(
+ self.json_diff_report, expected_output):
+ self.assertEqual(out['name'], expected['name'])
+ self.assertEqual(out['time_unit'], expected['time_unit'])
+ assert_utest(self, out, expected)
+ assert_measurements(self, out, expected)
+
+
+class TestReportDifferenceWithUTestWhileDisplayingAggregatesOnly(
+ unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ def load_results():
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput1 = os.path.join(testInputs, 'test3_run0.json')
+ testOutput2 = os.path.join(testInputs, 'test3_run1.json')
+ with open(testOutput1, 'r') as f:
+ json1 = json.load(f)
+ with open(testOutput2, 'r') as f:
+ json2 = json.load(f)
+ return json1, json2
+
+ json1, json2 = load_results()
+ cls.json_diff_report = get_difference_report(
+ json1, json2, utest=True)
+
+ def test_json_diff_report_pretty_printing(self):
+ expect_lines = [
+ ['BM_One', '-0.1000', '+0.1000', '10', '9', '100', '110'],
+ ['BM_Two', '+0.1111', '-0.0111', '9', '10', '90', '89'],
+ ['BM_Two', '-0.1250', '-0.1628', '8', '7', '86', '72'],
+ ['BM_Two_pvalue',
+ '0.6985',
+ '0.6985',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '2.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['short', '-0.1250', '-0.0625', '8', '7', '80', '75'],
+ ['short', '-0.4325', '-0.1351', '8', '5', '77', '67'],
+ ['short_pvalue',
+ '0.7671',
+ '0.1489',
+ 'U',
+ 'Test,',
+ 'Repetitions:',
+ '2',
+ 'vs',
+ '3.',
+ 'WARNING:',
+ 'Results',
+ 'unreliable!',
+ '9+',
+ 'repetitions',
+ 'recommended.'],
+ ['medium', '-0.3750', '-0.3375', '8', '5', '80', '53']
+ ]
+ output_lines_with_header = print_difference_report(
+ self.json_diff_report,
+ utest=True, utest_alpha=0.05, use_color=False)
+ output_lines = output_lines_with_header[2:]
+ print("\n")
+ print("\n".join(output_lines_with_header))
+ self.assertEqual(len(output_lines), len(expect_lines))
+ for i in range(0, len(output_lines)):
+ parts = [x for x in output_lines[i].split(' ') if x]
+ self.assertEqual(expect_lines[i], parts)
+
+ def test_json_diff_report(self):
+ expected_output = [
+ {
+ 'name': u'BM_One',
+ 'measurements': [
+ {'time': -0.1,
+ 'cpu': 0.1,
+ 'real_time': 10,
+ 'real_time_other': 9,
+ 'cpu_time': 100,
+ 'cpu_time_other': 110}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {}
+ },
+ {
+ 'name': u'BM_Two',
+ 'measurements': [
+ {'time': 0.1111111111111111,
+ 'cpu': -0.011111111111111112,
+ 'real_time': 9,
+ 'real_time_other': 10,
+ 'cpu_time': 90,
+ 'cpu_time_other': 89},
+ {'time': -0.125, 'cpu': -0.16279069767441862, 'real_time': 8,
+ 'real_time_other': 7, 'cpu_time': 86, 'cpu_time_other': 72}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {
+ 'have_optimal_repetitions': False, 'cpu_pvalue': 0.6985353583033387, 'time_pvalue': 0.6985353583033387
+ }
+ },
+ {
+ 'name': u'short',
+ 'measurements': [
+ {'time': -0.125,
+ 'cpu': -0.0625,
+ 'real_time': 8,
+ 'real_time_other': 7,
+ 'cpu_time': 80,
+ 'cpu_time_other': 75},
+ {'time': -0.4325,
+ 'cpu': -0.13506493506493514,
+ 'real_time': 8,
+ 'real_time_other': 4.54,
+ 'cpu_time': 77,
+ 'cpu_time_other': 66.6}
+ ],
+ 'time_unit': 'ns',
+ 'utest': {
+ 'have_optimal_repetitions': False, 'cpu_pvalue': 0.14891467317876572, 'time_pvalue': 0.7670968684102772
+ }
+ },
+ {
+ 'name': u'medium',
+ 'measurements': [
+ {'real_time_other': 5,
+ 'cpu_time': 80,
+ 'time': -0.375,
+ 'real_time': 8,
+ 'cpu_time_other': 53,
+ 'cpu': -0.3375
+ }
+ ],
+ 'utest': {},
+ 'time_unit': u'ns',
+ 'aggregate_name': ''
+ }
+ ]
+ self.assertEqual(len(self.json_diff_report), len(expected_output))
+ for out, expected in zip(
+ self.json_diff_report, expected_output):
+ self.assertEqual(out['name'], expected['name'])
+ self.assertEqual(out['time_unit'], expected['time_unit'])
+ assert_utest(self, out, expected)
+ assert_measurements(self, out, expected)
+
+
+class TestReportSorting(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ def load_result():
+ import json
+ testInputs = os.path.join(
+ os.path.dirname(
+ os.path.realpath(__file__)),
+ 'Inputs')
+ testOutput = os.path.join(testInputs, 'test4_run.json')
+ with open(testOutput, 'r') as f:
+ json = json.load(f)
+ return json
+
+ cls.json = load_result()
+
+ def test_json_diff_report_pretty_printing(self):
+ import util
+
+ expected_names = [
+ "99 family 0 instance 0 repetition 0",
+ "98 family 0 instance 0 repetition 1",
+ "97 family 0 instance 0 aggregate",
+ "96 family 0 instance 1 repetition 0",
+ "95 family 0 instance 1 repetition 1",
+ "94 family 0 instance 1 aggregate",
+ "93 family 1 instance 0 repetition 0",
+ "92 family 1 instance 0 repetition 1",
+ "91 family 1 instance 0 aggregate",
+ "90 family 1 instance 1 repetition 0",
+ "89 family 1 instance 1 repetition 1",
+ "88 family 1 instance 1 aggregate"
+ ]
+
+ for n in range(len(self.json['benchmarks']) ** 2):
+ random.shuffle(self.json['benchmarks'])
+ sorted_benchmarks = util.sort_benchmark_results(self.json)[
+ 'benchmarks']
+ self.assertEqual(len(expected_names), len(sorted_benchmarks))
+ for out, expected in zip(sorted_benchmarks, expected_names):
+ self.assertEqual(out['name'], expected)
+
+
+def assert_utest(unittest_instance, lhs, rhs):
+ if lhs['utest']:
+ unittest_instance.assertAlmostEqual(
+ lhs['utest']['cpu_pvalue'],
+ rhs['utest']['cpu_pvalue'])
+ unittest_instance.assertAlmostEqual(
+ lhs['utest']['time_pvalue'],
+ rhs['utest']['time_pvalue'])
+ unittest_instance.assertEqual(
+ lhs['utest']['have_optimal_repetitions'],
+ rhs['utest']['have_optimal_repetitions'])
+ else:
+ # lhs is empty. assert if rhs is not.
+ unittest_instance.assertEqual(lhs['utest'], rhs['utest'])
+
+
+def assert_measurements(unittest_instance, lhs, rhs):
+ for m1, m2 in zip(lhs['measurements'], rhs['measurements']):
+ unittest_instance.assertEqual(m1['real_time'], m2['real_time'])
+ unittest_instance.assertEqual(m1['cpu_time'], m2['cpu_time'])
+ # m1['time'] and m1['cpu'] hold values which are being calculated,
+ # and therefore we must use almost-equal pattern.
+ unittest_instance.assertAlmostEqual(m1['time'], m2['time'], places=4)
+ unittest_instance.assertAlmostEqual(m1['cpu'], m2['cpu'], places=4)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;
--- /dev/null
+"""util.py - General utilities for running, loading, and processing benchmarks
+"""
+import json
+import os
+import tempfile
+import subprocess
+import sys
+import functools
+
+# Input file type enumeration
+IT_Invalid = 0
+IT_JSON = 1
+IT_Executable = 2
+
+_num_magic_bytes = 2 if sys.platform.startswith('win') else 4
+
+
+def is_executable_file(filename):
+ """
+ Return 'True' if 'filename' names a valid file which is likely
+ an executable. A file is considered an executable if it starts with the
+ magic bytes for a EXE, Mach O, or ELF file.
+ """
+ if not os.path.isfile(filename):
+ return False
+ with open(filename, mode='rb') as f:
+ magic_bytes = f.read(_num_magic_bytes)
+ if sys.platform == 'darwin':
+ return magic_bytes in [
+ b'\xfe\xed\xfa\xce', # MH_MAGIC
+ b'\xce\xfa\xed\xfe', # MH_CIGAM
+ b'\xfe\xed\xfa\xcf', # MH_MAGIC_64
+ b'\xcf\xfa\xed\xfe', # MH_CIGAM_64
+ b'\xca\xfe\xba\xbe', # FAT_MAGIC
+ b'\xbe\xba\xfe\xca' # FAT_CIGAM
+ ]
+ elif sys.platform.startswith('win'):
+ return magic_bytes == b'MZ'
+ else:
+ return magic_bytes == b'\x7FELF'
+
+
+def is_json_file(filename):
+ """
+ Returns 'True' if 'filename' names a valid JSON output file.
+ 'False' otherwise.
+ """
+ try:
+ with open(filename, 'r') as f:
+ json.load(f)
+ return True
+ except BaseException:
+ pass
+ return False
+
+
+def classify_input_file(filename):
+ """
+ Return a tuple (type, msg) where 'type' specifies the classified type
+ of 'filename'. If 'type' is 'IT_Invalid' then 'msg' is a human readable
+ string represeting the error.
+ """
+ ftype = IT_Invalid
+ err_msg = None
+ if not os.path.exists(filename):
+ err_msg = "'%s' does not exist" % filename
+ elif not os.path.isfile(filename):
+ err_msg = "'%s' does not name a file" % filename
+ elif is_executable_file(filename):
+ ftype = IT_Executable
+ elif is_json_file(filename):
+ ftype = IT_JSON
+ else:
+ err_msg = "'%s' does not name a valid benchmark executable or JSON file" % filename
+ return ftype, err_msg
+
+
+def check_input_file(filename):
+ """
+ Classify the file named by 'filename' and return the classification.
+ If the file is classified as 'IT_Invalid' print an error message and exit
+ the program.
+ """
+ ftype, msg = classify_input_file(filename)
+ if ftype == IT_Invalid:
+ print("Invalid input file: %s" % msg)
+ sys.exit(1)
+ return ftype
+
+
+def find_benchmark_flag(prefix, benchmark_flags):
+ """
+ Search the specified list of flags for a flag matching `<prefix><arg>` and
+ if it is found return the arg it specifies. If specified more than once the
+ last value is returned. If the flag is not found None is returned.
+ """
+ assert prefix.startswith('--') and prefix.endswith('=')
+ result = None
+ for f in benchmark_flags:
+ if f.startswith(prefix):
+ result = f[len(prefix):]
+ return result
+
+
+def remove_benchmark_flags(prefix, benchmark_flags):
+ """
+ Return a new list containing the specified benchmark_flags except those
+ with the specified prefix.
+ """
+ assert prefix.startswith('--') and prefix.endswith('=')
+ return [f for f in benchmark_flags if not f.startswith(prefix)]
+
+
+def load_benchmark_results(fname):
+ """
+ Read benchmark output from a file and return the JSON object.
+ REQUIRES: 'fname' names a file containing JSON benchmark output.
+ """
+ with open(fname, 'r') as f:
+ return json.load(f)
+
+
+def sort_benchmark_results(result):
+ benchmarks = result['benchmarks']
+
+ # From inner key to the outer key!
+ benchmarks = sorted(
+ benchmarks, key=lambda benchmark: benchmark['repetition_index'] if 'repetition_index' in benchmark else -1)
+ benchmarks = sorted(
+ benchmarks, key=lambda benchmark: 1 if 'run_type' in benchmark and benchmark['run_type'] == "aggregate" else 0)
+ benchmarks = sorted(
+ benchmarks, key=lambda benchmark: benchmark['per_family_instance_index'] if 'per_family_instance_index' in benchmark else -1)
+ benchmarks = sorted(
+ benchmarks, key=lambda benchmark: benchmark['family_index'] if 'family_index' in benchmark else -1)
+
+ result['benchmarks'] = benchmarks
+ return result
+
+
+def run_benchmark(exe_name, benchmark_flags):
+ """
+ Run a benchmark specified by 'exe_name' with the specified
+ 'benchmark_flags'. The benchmark is run directly as a subprocess to preserve
+ real time console output.
+ RETURNS: A JSON object representing the benchmark output
+ """
+ output_name = find_benchmark_flag('--benchmark_out=',
+ benchmark_flags)
+ is_temp_output = False
+ if output_name is None:
+ is_temp_output = True
+ thandle, output_name = tempfile.mkstemp()
+ os.close(thandle)
+ benchmark_flags = list(benchmark_flags) + \
+ ['--benchmark_out=%s' % output_name]
+
+ cmd = [exe_name] + benchmark_flags
+ print("RUNNING: %s" % ' '.join(cmd))
+ exitCode = subprocess.call(cmd)
+ if exitCode != 0:
+ print('TEST FAILED...')
+ sys.exit(exitCode)
+ json_res = load_benchmark_results(output_name)
+ if is_temp_output:
+ os.unlink(output_name)
+ return json_res
+
+
+def run_or_load_benchmark(filename, benchmark_flags):
+ """
+ Get the results for a specified benchmark. If 'filename' specifies
+ an executable benchmark then the results are generated by running the
+ benchmark. Otherwise 'filename' must name a valid JSON output file,
+ which is loaded and the result returned.
+ """
+ ftype = check_input_file(filename)
+ if ftype == IT_JSON:
+ return load_benchmark_results(filename)
+ if ftype == IT_Executable:
+ return run_benchmark(filename, benchmark_flags)
+ raise ValueError('Unknown file type %s' % ftype)
--- /dev/null
+scipy>=1.5.0
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+
+"""
+strip_asm.py - Cleanup ASM output for the specified file
+"""
+
+from argparse import ArgumentParser
+import sys
+import os
+import re
+
+def find_used_labels(asm):
+ found = set()
+ label_re = re.compile("\s*j[a-z]+\s+\.L([a-zA-Z0-9][a-zA-Z0-9_]*)")
+ for l in asm.splitlines():
+ m = label_re.match(l)
+ if m:
+ found.add('.L%s' % m.group(1))
+ return found
+
+
+def normalize_labels(asm):
+ decls = set()
+ label_decl = re.compile("^[.]{0,1}L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")
+ for l in asm.splitlines():
+ m = label_decl.match(l)
+ if m:
+ decls.add(m.group(0))
+ if len(decls) == 0:
+ return asm
+ needs_dot = next(iter(decls))[0] != '.'
+ if not needs_dot:
+ return asm
+ for ld in decls:
+ asm = re.sub("(^|\s+)" + ld + "(?=:|\s)", '\\1.' + ld, asm)
+ return asm
+
+
+def transform_labels(asm):
+ asm = normalize_labels(asm)
+ used_decls = find_used_labels(asm)
+ new_asm = ''
+ label_decl = re.compile("^\.L([a-zA-Z0-9][a-zA-Z0-9_]*)(?=:)")
+ for l in asm.splitlines():
+ m = label_decl.match(l)
+ if not m or m.group(0) in used_decls:
+ new_asm += l
+ new_asm += '\n'
+ return new_asm
+
+
+def is_identifier(tk):
+ if len(tk) == 0:
+ return False
+ first = tk[0]
+ if not first.isalpha() and first != '_':
+ return False
+ for i in range(1, len(tk)):
+ c = tk[i]
+ if not c.isalnum() and c != '_':
+ return False
+ return True
+
+def process_identifiers(l):
+ """
+ process_identifiers - process all identifiers and modify them to have
+ consistent names across all platforms; specifically across ELF and MachO.
+ For example, MachO inserts an additional understore at the beginning of
+ names. This function removes that.
+ """
+ parts = re.split(r'([a-zA-Z0-9_]+)', l)
+ new_line = ''
+ for tk in parts:
+ if is_identifier(tk):
+ if tk.startswith('__Z'):
+ tk = tk[1:]
+ elif tk.startswith('_') and len(tk) > 1 and \
+ tk[1].isalpha() and tk[1] != 'Z':
+ tk = tk[1:]
+ new_line += tk
+ return new_line
+
+
+def process_asm(asm):
+ """
+ Strip the ASM of unwanted directives and lines
+ """
+ new_contents = ''
+ asm = transform_labels(asm)
+
+ # TODO: Add more things we want to remove
+ discard_regexes = [
+ re.compile("\s+\..*$"), # directive
+ re.compile("\s*#(NO_APP|APP)$"), #inline ASM
+ re.compile("\s*#.*$"), # comment line
+ re.compile("\s*\.globa?l\s*([.a-zA-Z_][a-zA-Z0-9$_.]*)"), #global directive
+ re.compile("\s*\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)"),
+ ]
+ keep_regexes = [
+
+ ]
+ fn_label_def = re.compile("^[a-zA-Z_][a-zA-Z0-9_.]*:")
+ for l in asm.splitlines():
+ # Remove Mach-O attribute
+ l = l.replace('@GOTPCREL', '')
+ add_line = True
+ for reg in discard_regexes:
+ if reg.match(l) is not None:
+ add_line = False
+ break
+ for reg in keep_regexes:
+ if reg.match(l) is not None:
+ add_line = True
+ break
+ if add_line:
+ if fn_label_def.match(l) and len(new_contents) != 0:
+ new_contents += '\n'
+ l = process_identifiers(l)
+ new_contents += l
+ new_contents += '\n'
+ return new_contents
+
+def main():
+ parser = ArgumentParser(
+ description='generate a stripped assembly file')
+ parser.add_argument(
+ 'input', metavar='input', type=str, nargs=1,
+ help='An input assembly file')
+ parser.add_argument(
+ 'out', metavar='output', type=str, nargs=1,
+ help='The output file')
+ args, unknown_args = parser.parse_known_args()
+ input = args.input[0]
+ output = args.out[0]
+ if not os.path.isfile(input):
+ print(("ERROR: input file '%s' does not exist") % input)
+ sys.exit(1)
+ contents = None
+ with open(input, 'r') as f:
+ contents = f.read()
+ new_contents = process_asm(contents)
+ with open(output, 'w') as f:
+ f.write(new_contents)
+
+
+if __name__ == '__main__':
+ main()
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+# kate: tab-width: 4; replace-tabs on; indent-width 4; tab-indents: off;
+# kate: indent-mode python; remove-trailing-spaces modified;
--- /dev/null
+//===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
+#define LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
+
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/Reader.h"
+#include "lld/Core/Writer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <set>
+
+using llvm::MachO::HeaderFileType;
+
+namespace lld {
+
+namespace mach_o {
+class ArchHandler;
+class MachODylibFile;
+class MachOFile;
+class SectCreateFile;
+}
+
+class MachOLinkingContext : public LinkingContext {
+public:
+ MachOLinkingContext();
+ ~MachOLinkingContext() override;
+
+ enum Arch {
+ arch_unknown,
+ arch_ppc,
+ arch_x86,
+ arch_x86_64,
+ arch_armv6,
+ arch_armv7,
+ arch_armv7s,
+ arch_arm64,
+ };
+
+ enum class OS {
+ unknown,
+ macOSX,
+ iOS,
+ iOS_simulator
+ };
+
+ enum class ExportMode {
+ globals, // Default, all global symbols exported.
+ exported, // -exported_symbol[s_list], only listed symbols exported.
+ unexported // -unexported_symbol[s_list], no listed symbol exported.
+ };
+
+ enum class DebugInfoMode {
+ addDebugMap, // Default
+ noDebugMap // -S option
+ };
+
+ enum class UndefinedMode {
+ error,
+ warning,
+ suppress,
+ dynamicLookup
+ };
+
+ enum ObjCConstraint {
+ objc_unknown = 0,
+ objc_supports_gc = 2,
+ objc_gc_only = 4,
+ // Image optimized by dyld = 8
+ // GC compaction = 16
+ objc_retainReleaseForSimulator = 32,
+ objc_retainRelease
+ };
+
+ /// Initializes the context to sane default values given the specified output
+ /// file type, arch, os, and minimum os version. This should be called before
+ /// other setXXX() methods.
+ void configure(HeaderFileType type, Arch arch, OS os, uint32_t minOSVersion,
+ bool exportDynamicSymbols);
+
+ void addPasses(PassManager &pm) override;
+ bool validateImpl() override;
+ std::string demangle(StringRef symbolName) const override;
+
+ void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
+
+ /// Creates a new file which is owned by the context. Returns a pointer to
+ /// the new file.
+ template <class T, class... Args>
+ typename std::enable_if<!std::is_array<T>::value, T *>::type
+ make_file(Args &&... args) const {
+ auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ auto *filePtr = file.get();
+ auto *ctx = const_cast<MachOLinkingContext *>(this);
+ ctx->getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
+ return filePtr;
+ }
+
+ uint32_t getCPUType() const;
+ uint32_t getCPUSubType() const;
+
+ bool addEntryPointLoadCommand() const;
+ bool addUnixThreadLoadCommand() const;
+ bool outputTypeHasEntry() const;
+ bool is64Bit() const;
+
+ virtual uint64_t pageZeroSize() const { return _pageZeroSize; }
+ virtual uint64_t pageSize() const { return _pageSize; }
+
+ mach_o::ArchHandler &archHandler() const;
+
+ HeaderFileType outputMachOType() const { return _outputMachOType; }
+
+ Arch arch() const { return _arch; }
+ StringRef archName() const { return nameFromArch(_arch); }
+ OS os() const { return _os; }
+
+ ExportMode exportMode() const { return _exportMode; }
+ void setExportMode(ExportMode mode) { _exportMode = mode; }
+ void addExportSymbol(StringRef sym);
+ bool exportRestrictMode() const { return _exportMode != ExportMode::globals; }
+ bool exportSymbolNamed(StringRef sym) const;
+
+ DebugInfoMode debugInfoMode() const { return _debugInfoMode; }
+ void setDebugInfoMode(DebugInfoMode mode) {
+ _debugInfoMode = mode;
+ }
+
+ void appendOrderedSymbol(StringRef symbol, StringRef filename);
+
+ bool keepPrivateExterns() const { return _keepPrivateExterns; }
+ void setKeepPrivateExterns(bool v) { _keepPrivateExterns = v; }
+ bool demangleSymbols() const { return _demangle; }
+ void setDemangleSymbols(bool d) { _demangle = d; }
+ bool mergeObjCCategories() const { return _mergeObjCCategories; }
+ void setMergeObjCCategories(bool v) { _mergeObjCCategories = v; }
+ /// Create file at specified path which will contain a binary encoding
+ /// of all input and output file paths.
+ std::error_code createDependencyFile(StringRef path);
+ void addInputFileDependency(StringRef path) const;
+ void addInputFileNotFound(StringRef path) const;
+ void addOutputFileDependency(StringRef path) const;
+
+ bool minOS(StringRef mac, StringRef iOS) const;
+ void setDoNothing(bool value) { _doNothing = value; }
+ bool doNothing() const { return _doNothing; }
+ bool printAtoms() const { return _printAtoms; }
+ bool testingFileUsage() const { return _testingFileUsage; }
+ const StringRefVector &searchDirs() const { return _searchDirs; }
+ const StringRefVector &frameworkDirs() const { return _frameworkDirs; }
+ void setSysLibRoots(const StringRefVector &paths);
+ const StringRefVector &sysLibRoots() const { return _syslibRoots; }
+ bool PIE() const { return _pie; }
+ void setPIE(bool pie) { _pie = pie; }
+ bool generateVersionLoadCommand() const {
+ return _generateVersionLoadCommand;
+ }
+ void setGenerateVersionLoadCommand(bool v) {
+ _generateVersionLoadCommand = v;
+ }
+
+ bool generateFunctionStartsLoadCommand() const {
+ return _generateFunctionStartsLoadCommand;
+ }
+ void setGenerateFunctionStartsLoadCommand(bool v) {
+ _generateFunctionStartsLoadCommand = v;
+ }
+
+ bool generateDataInCodeLoadCommand() const {
+ return _generateDataInCodeLoadCommand;
+ }
+ void setGenerateDataInCodeLoadCommand(bool v) {
+ _generateDataInCodeLoadCommand = v;
+ }
+
+ uint64_t stackSize() const { return _stackSize; }
+ void setStackSize(uint64_t stackSize) { _stackSize = stackSize; }
+
+ uint64_t baseAddress() const { return _baseAddress; }
+ void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; }
+
+ ObjCConstraint objcConstraint() const { return _objcConstraint; }
+
+ uint32_t osMinVersion() const { return _osMinVersion; }
+
+ uint32_t sdkVersion() const { return _sdkVersion; }
+ void setSdkVersion(uint64_t v) { _sdkVersion = v; }
+
+ uint64_t sourceVersion() const { return _sourceVersion; }
+ void setSourceVersion(uint64_t v) { _sourceVersion = v; }
+
+ uint32_t swiftVersion() const { return _swiftVersion; }
+
+ /// Checks whether a given path on the filesystem exists.
+ ///
+ /// When running in -test_file_usage mode, this method consults an
+ /// internally maintained list of files that exist (provided by -path_exists)
+ /// instead of the actual filesystem.
+ bool pathExists(StringRef path) const;
+
+ /// Like pathExists() but only used on files - not directories.
+ bool fileExists(StringRef path) const;
+
+ /// Adds any library search paths derived from the given base, possibly
+ /// modified by -syslibroots.
+ ///
+ /// The set of paths added consists of approximately all syslibroot-prepended
+ /// versions of libPath that exist, or the original libPath if there are none
+ /// for whatever reason. With various edge-cases for compatibility.
+ void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false);
+
+ /// Determine whether -lFoo can be resolve within the given path, and
+ /// return the filename if so.
+ ///
+ /// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in
+ /// that order, unless Foo ends in ".o", in which case only the exact file
+ /// matches (e.g. -lfoo.o would only find foo.o).
+ llvm::Optional<StringRef> searchDirForLibrary(StringRef path,
+ StringRef libName) const;
+
+ /// Iterates through all search path entries looking for libName (as
+ /// specified by -lFoo).
+ llvm::Optional<StringRef> searchLibrary(StringRef libName) const;
+
+ /// Add a framework search path. Internally, this method may be prepended
+ /// the path with syslibroot.
+ void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false);
+
+ /// Iterates through all framework directories looking for
+ /// Foo.framework/Foo (when fwName = "Foo").
+ llvm::Optional<StringRef> findPathForFramework(StringRef fwName) const;
+
+ /// The dylib's binary compatibility version, in the raw uint32 format.
+ ///
+ /// When building a dynamic library, this is the compatibility version that
+ /// gets embedded into the result. Other Mach-O binaries that link against
+ /// this library will store the compatibility version in its load command. At
+ /// runtime, the loader will verify that the binary is compatible with the
+ /// installed dynamic library.
+ uint32_t compatibilityVersion() const { return _compatibilityVersion; }
+
+ /// The dylib's current version, in the raw uint32 format.
+ ///
+ /// When building a dynamic library, this is the current version that gets
+ /// embedded into the result. Other Mach-O binaries that link against
+ /// this library will store the compatibility version in its load command.
+ uint32_t currentVersion() const { return _currentVersion; }
+
+ /// The dylib's install name.
+ ///
+ /// Binaries that link against the dylib will embed this path into the dylib
+ /// load command. When loading the binaries at runtime, this is the location
+ /// on disk that the loader will look for the dylib.
+ StringRef installName() const { return _installName; }
+
+ /// Whether or not the dylib has side effects during initialization.
+ ///
+ /// Dylibs marked as being dead strippable provide the guarantee that loading
+ /// the dylib has no side effects, allowing the linker to strip out the dylib
+ /// when linking a binary that does not use any of its symbols.
+ bool deadStrippableDylib() const { return _deadStrippableDylib; }
+
+ /// Whether or not to use flat namespace.
+ ///
+ /// MachO usually uses a two-level namespace, where each external symbol
+ /// referenced by the target is associated with the dylib that will provide
+ /// the symbol's definition at runtime. Using flat namespace overrides this
+ /// behavior: the linker searches all dylibs on the command line and all
+ /// dylibs those original dylibs depend on, but does not record which dylib
+ /// an external symbol came from. At runtime dyld again searches all images
+ /// and uses the first definition it finds. In addition, any undefines in
+ /// loaded flat_namespace dylibs must be resolvable at build time.
+ bool useFlatNamespace() const { return _flatNamespace; }
+
+ /// How to handle undefined symbols.
+ ///
+ /// Options are:
+ /// * error: Report an error and terminate linking.
+ /// * warning: Report a warning, but continue linking.
+ /// * suppress: Ignore and continue linking.
+ /// * dynamic_lookup: For use with -twolevel namespace: Records source dylibs
+ /// for symbols that are defined in a linked dylib at static link time.
+ /// Undefined symbols are handled by searching all loaded images at
+ /// runtime.
+ UndefinedMode undefinedMode() const { return _undefinedMode; }
+
+ /// The path to the executable that will load the bundle at runtime.
+ ///
+ /// When building a Mach-O bundle, this executable will be examined if there
+ /// are undefined symbols after the main link phase. It is expected that this
+ /// binary will be loading the bundle at runtime and will provide the symbols
+ /// at that point.
+ StringRef bundleLoader() const { return _bundleLoader; }
+
+ void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; }
+ void setCurrentVersion(uint32_t vers) { _currentVersion = vers; }
+ void setInstallName(StringRef name) { _installName = name; }
+ void setDeadStrippableDylib(bool deadStrippable) {
+ _deadStrippableDylib = deadStrippable;
+ }
+ void setUseFlatNamespace(bool flatNamespace) {
+ _flatNamespace = flatNamespace;
+ }
+
+ void setUndefinedMode(UndefinedMode undefinedMode) {
+ _undefinedMode = undefinedMode;
+ }
+
+ void setBundleLoader(StringRef loader) { _bundleLoader = loader; }
+ void setPrintAtoms(bool value=true) { _printAtoms = value; }
+ void setTestingFileUsage(bool value = true) {
+ _testingFileUsage = value;
+ }
+ void addExistingPathForDebug(StringRef path) {
+ _existingPaths.insert(path);
+ }
+
+ void addRpath(StringRef rpath);
+ const StringRefVector &rpaths() const { return _rpaths; }
+
+ /// Add section alignment constraint on final layout.
+ void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align);
+
+ /// Add a section based on a command-line sectcreate option.
+ void addSectCreateSection(StringRef seg, StringRef sect,
+ std::unique_ptr<MemoryBuffer> content);
+
+ /// Returns true if specified section had alignment constraints.
+ bool sectionAligned(StringRef seg, StringRef sect, uint16_t &align) const;
+
+ StringRef dyldPath() const { return "/usr/lib/dyld"; }
+
+ /// Stub creation Pass should be run.
+ bool needsStubsPass() const;
+
+ // GOT creation Pass should be run.
+ bool needsGOTPass() const;
+
+ /// Pass to add TLV sections.
+ bool needsTLVPass() const;
+
+ /// Pass to transform __compact_unwind into __unwind_info should be run.
+ bool needsCompactUnwindPass() const;
+
+ /// Pass to add shims switching between thumb and arm mode.
+ bool needsShimPass() const;
+
+ /// Pass to add objc image info and optimized objc data.
+ bool needsObjCPass() const;
+
+ /// Magic symbol name stubs will need to help lazy bind.
+ StringRef binderSymbolName() const;
+
+ /// Used to keep track of direct and indirect dylibs.
+ void registerDylib(mach_o::MachODylibFile *dylib, bool upward) const;
+
+ // Reads a file from disk to memory. Returns only a needed chunk
+ // if a fat binary.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> getMemoryBuffer(StringRef path);
+
+ /// Used to find indirect dylibs. Instantiates a MachODylibFile if one
+ /// has not already been made for the requested dylib. Uses -L and -F
+ /// search paths to allow indirect dylibs to be overridden.
+ mach_o::MachODylibFile* findIndirectDylib(StringRef path);
+
+ uint32_t dylibCurrentVersion(StringRef installName) const;
+
+ uint32_t dylibCompatVersion(StringRef installName) const;
+
+ ArrayRef<mach_o::MachODylibFile*> allDylibs() const {
+ return _allDylibs;
+ }
+
+ /// Creates a copy (owned by this MachOLinkingContext) of a string.
+ StringRef copy(StringRef str) { return str.copy(_allocator); }
+
+ /// If the memoryBuffer is a fat file with a slice for the current arch,
+ /// this method will return the offset and size of that slice.
+ bool sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size);
+
+ /// Returns if a command line option specified dylib is an upward link.
+ bool isUpwardDylib(StringRef installName) const;
+
+ static bool isThinObjectFile(StringRef path, Arch &arch);
+ static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
+ static Arch archFromName(StringRef archName);
+ static StringRef nameFromArch(Arch arch);
+ static uint32_t cpuTypeFromArch(Arch arch);
+ static uint32_t cpuSubtypeFromArch(Arch arch);
+ static bool is64Bit(Arch arch);
+ static bool isHostEndian(Arch arch);
+ static bool isBigEndian(Arch arch);
+
+ /// Construct 32-bit value from string "X.Y.Z" where
+ /// bits are xxxx.yy.zz. Largest number is 65535.255.255
+ static bool parsePackedVersion(StringRef str, uint32_t &result);
+
+ /// Construct 64-bit value from string "A.B.C.D.E" where
+ /// bits are aaaa.bb.cc.dd.ee. Largest number is 16777215.1023.1023.1023.1023
+ static bool parsePackedVersion(StringRef str, uint64_t &result);
+
+ void finalizeInputFiles() override;
+
+ llvm::Error handleLoadedFile(File &file) override;
+
+ bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right,
+ bool &leftBeforeRight) const;
+
+ /// Return the 'flat namespace' file. This is the file that supplies
+ /// atoms for otherwise undefined symbols when the -flat_namespace or
+ /// -undefined dynamic_lookup options are used.
+ File* flatNamespaceFile() const { return _flatNamespaceFile; }
+
+private:
+ Writer &writer() const override;
+ mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
+ struct ArchInfo {
+ StringRef archName;
+ MachOLinkingContext::Arch arch;
+ bool littleEndian;
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ };
+
+ struct SectionAlign {
+ StringRef segmentName;
+ StringRef sectionName;
+ uint16_t align;
+ };
+
+ struct OrderFileNode {
+ StringRef fileFilter;
+ unsigned order;
+ };
+
+ static bool findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
+ const DefinedAtom *atom, unsigned &ordinal);
+
+ static ArchInfo _s_archInfos[];
+
+ std::set<StringRef> _existingPaths; // For testing only.
+ StringRefVector _searchDirs;
+ StringRefVector _syslibRoots;
+ StringRefVector _frameworkDirs;
+ HeaderFileType _outputMachOType = llvm::MachO::MH_EXECUTE;
+ bool _outputMachOTypeStatic = false; // Disambiguate static vs dynamic prog
+ bool _doNothing = false; // for -help and -v which just print info
+ bool _pie = false;
+ Arch _arch = arch_unknown;
+ OS _os = OS::macOSX;
+ uint32_t _osMinVersion = 0;
+ uint32_t _sdkVersion = 0;
+ uint64_t _sourceVersion = 0;
+ uint64_t _pageZeroSize = 0;
+ uint64_t _pageSize = 4096;
+ uint64_t _baseAddress = 0;
+ uint64_t _stackSize = 0;
+ uint32_t _compatibilityVersion = 0;
+ uint32_t _currentVersion = 0;
+ ObjCConstraint _objcConstraint = objc_unknown;
+ uint32_t _swiftVersion = 0;
+ StringRef _installName;
+ StringRefVector _rpaths;
+ bool _flatNamespace = false;
+ UndefinedMode _undefinedMode = UndefinedMode::error;
+ bool _deadStrippableDylib = false;
+ bool _printAtoms = false;
+ bool _testingFileUsage = false;
+ bool _keepPrivateExterns = false;
+ bool _demangle = false;
+ bool _mergeObjCCategories = true;
+ bool _generateVersionLoadCommand = false;
+ bool _generateFunctionStartsLoadCommand = false;
+ bool _generateDataInCodeLoadCommand = false;
+ StringRef _bundleLoader;
+ mutable std::unique_ptr<mach_o::ArchHandler> _archHandler;
+ mutable std::unique_ptr<Writer> _writer;
+ std::vector<SectionAlign> _sectAligns;
+ mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
+ mutable std::vector<mach_o::MachODylibFile*> _allDylibs;
+ mutable std::set<mach_o::MachODylibFile*> _upwardDylibs;
+ mutable std::vector<std::unique_ptr<File>> _indirectDylibs;
+ mutable std::mutex _dylibsMutex;
+ ExportMode _exportMode = ExportMode::globals;
+ llvm::StringSet<> _exportedSymbols;
+ DebugInfoMode _debugInfoMode = DebugInfoMode::addDebugMap;
+ std::unique_ptr<llvm::raw_fd_ostream> _dependencyInfo;
+ llvm::StringMap<std::vector<OrderFileNode>> _orderFiles;
+ unsigned _orderFileEntries = 0;
+ File *_flatNamespaceFile = nullptr;
+ mach_o::SectCreateFile *_sectCreateFile = nullptr;
+};
+
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
--- /dev/null
+//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_YAML_CONTEXT_H
+#define LLD_READER_WRITER_YAML_CONTEXT_H
+
+#include "lld/Common/LLVM.h"
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace lld {
+class File;
+class LinkingContext;
+class Registry;
+namespace mach_o {
+namespace normalized {
+struct NormalizedFile;
+}
+}
+
+using lld::mach_o::normalized::NormalizedFile;
+
+/// When YAML I/O is used in lld, the yaml context always holds a YamlContext
+/// object. We need to support hetergenous yaml documents which each require
+/// different context info. This struct supports all clients.
+struct YamlContext {
+ const LinkingContext *_ctx = nullptr;
+ const Registry *_registry = nullptr;
+ File *_file = nullptr;
+ NormalizedFile *_normalizeMachOFile = nullptr;
+ StringRef _path;
+};
+
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_YAML_CONTEXT_H
--- /dev/null
+add_subdirectory(Core)
+add_subdirectory(Driver)
+add_subdirectory(ReaderWriter)
--- /dev/null
+add_lld_library(lldCore
+ DefinedAtom.cpp
+ Error.cpp
+ File.cpp
+ LinkingContext.cpp
+ Reader.cpp
+ Resolver.cpp
+ SymbolTable.cpp
+ Writer.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${LLD_INCLUDE_DIR}/lld/Core
+
+ LINK_COMPONENTS
+ BinaryFormat
+ MC
+ Support
+
+ LINK_LIBS
+ ${LLVM_PTHREAD_LIB}
+
+ DEPENDS
+ intrinsics_gen
+ )
--- /dev/null
+//===- DefinedAtom.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/ErrorHandling.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+
+namespace lld {
+
+DefinedAtom::ContentPermissions DefinedAtom::permissions() const {
+ // By default base permissions on content type.
+ return permissions(this->contentType());
+}
+
+// Utility function for deriving permissions from content type
+DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
+ switch (type) {
+ case typeCode:
+ case typeResolver:
+ case typeBranchIsland:
+ case typeBranchShim:
+ case typeStub:
+ case typeStubHelper:
+ case typeMachHeader:
+ return permR_X;
+
+ case typeConstant:
+ case typeCString:
+ case typeUTF16String:
+ case typeCFI:
+ case typeLSDA:
+ case typeLiteral4:
+ case typeLiteral8:
+ case typeLiteral16:
+ case typeDTraceDOF:
+ case typeCompactUnwindInfo:
+ case typeProcessedUnwindInfo:
+ case typeObjCImageInfo:
+ case typeObjCMethodList:
+ return permR__;
+
+ case typeData:
+ case typeDataFast:
+ case typeZeroFill:
+ case typeZeroFillFast:
+ case typeObjC1Class:
+ case typeLazyPointer:
+ case typeLazyDylibPointer:
+ case typeNonLazyPointer:
+ case typeThunkTLV:
+ return permRW_;
+
+ case typeGOT:
+ case typeConstData:
+ case typeCFString:
+ case typeInitializerPtr:
+ case typeTerminatorPtr:
+ case typeCStringPtr:
+ case typeObjCClassPtr:
+ case typeObjC2CategoryList:
+ case typeInterposingTuples:
+ case typeTLVInitialData:
+ case typeTLVInitialZeroFill:
+ case typeTLVInitializerPtr:
+ return permRW_L;
+
+ case typeUnknown:
+ case typeTempLTO:
+ case typeSectCreate:
+ case typeDSOHandle:
+ return permUnknown;
+ }
+ llvm_unreachable("unknown content type");
+}
+
+} // namespace
--- /dev/null
+//===- Error.cpp - system_error extensions for lld --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Error.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <mutex>
+#include <string>
+#include <vector>
+
+using namespace lld;
+
+namespace {
+class _YamlReaderErrorCategory : public std::error_category {
+public:
+ const char* name() const noexcept override {
+ return "lld.yaml.reader";
+ }
+
+ std::string message(int ev) const override {
+ switch (static_cast<YamlReaderError>(ev)) {
+ case YamlReaderError::unknown_keyword:
+ return "Unknown keyword found in yaml file";
+ case YamlReaderError::illegal_value:
+ return "Bad value found in yaml file";
+ }
+ llvm_unreachable("An enumerator of YamlReaderError does not have a "
+ "message defined.");
+ }
+};
+} // end anonymous namespace
+
+const std::error_category &lld::YamlReaderCategory() {
+ static _YamlReaderErrorCategory o;
+ return o;
+}
+
+namespace lld {
+
+/// Temporary class to enable make_dynamic_error_code() until
+/// llvm::ErrorOr<> is updated to work with error encapsulations
+/// other than error_code.
+class dynamic_error_category : public std::error_category {
+public:
+ ~dynamic_error_category() override = default;
+
+ const char *name() const noexcept override {
+ return "lld.dynamic_error";
+ }
+
+ std::string message(int ev) const override {
+ assert(ev >= 0);
+ assert(ev < (int)_messages.size());
+ // The value is an index into the string vector.
+ return _messages[ev];
+ }
+
+ int add(std::string msg) {
+ std::lock_guard<std::recursive_mutex> lock(_mutex);
+ // Value zero is always the success value.
+ if (_messages.empty())
+ _messages.push_back("Success");
+ _messages.push_back(msg);
+ // Return the index of the string just appended.
+ return _messages.size() - 1;
+ }
+
+private:
+ std::vector<std::string> _messages;
+ std::recursive_mutex _mutex;
+};
+
+static dynamic_error_category categorySingleton;
+
+std::error_code make_dynamic_error_code(StringRef msg) {
+ return std::error_code(categorySingleton.add(std::string(msg)),
+ categorySingleton);
+}
+
+char GenericError::ID = 0;
+
+GenericError::GenericError(Twine Msg) : Msg(Msg.str()) { }
+
+void GenericError::log(raw_ostream &OS) const {
+ OS << Msg;
+}
+
+} // namespace lld
--- /dev/null
+//===- Core/File.cpp - A Container of Atoms -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/File.h"
+#include <mutex>
+
+namespace lld {
+
+File::~File() = default;
+
+File::AtomVector<DefinedAtom> File::_noDefinedAtoms;
+File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms;
+File::AtomVector<SharedLibraryAtom> File::_noSharedLibraryAtoms;
+File::AtomVector<AbsoluteAtom> File::_noAbsoluteAtoms;
+
+std::error_code File::parse() {
+ std::lock_guard<std::mutex> lock(_parseMutex);
+ if (!_lastError.hasValue())
+ _lastError = doParse();
+ return _lastError.getValue();
+}
+
+} // end namespace lld
--- /dev/null
+//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Node.h"
+#include "lld/Core/Simple.h"
+#include "lld/Core/Writer.h"
+#include <algorithm>
+
+namespace lld {
+
+LinkingContext::LinkingContext() = default;
+
+LinkingContext::~LinkingContext() = default;
+
+bool LinkingContext::validate() {
+ return validateImpl();
+}
+
+llvm::Error LinkingContext::writeFile(const File &linkedFile) const {
+ return this->writer().writeFile(linkedFile, _outputPath);
+}
+
+std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {
+ return createEntrySymbolFile("<command line option -e>");
+}
+
+std::unique_ptr<File>
+LinkingContext::createEntrySymbolFile(StringRef filename) const {
+ if (entrySymbolName().empty())
+ return nullptr;
+ std::unique_ptr<SimpleFile> entryFile(new SimpleFile(filename,
+ File::kindEntryObject));
+ entryFile->addAtom(
+ *(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
+ return std::move(entryFile);
+}
+
+std::unique_ptr<File> LinkingContext::createUndefinedSymbolFile() const {
+ return createUndefinedSymbolFile("<command line option -u or --defsym>");
+}
+
+std::unique_ptr<File>
+LinkingContext::createUndefinedSymbolFile(StringRef filename) const {
+ if (_initialUndefinedSymbols.empty())
+ return nullptr;
+ std::unique_ptr<SimpleFile> undefinedSymFile(
+ new SimpleFile(filename, File::kindUndefinedSymsObject));
+ for (StringRef undefSym : _initialUndefinedSymbols)
+ undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
+ *undefinedSymFile, undefSym)));
+ return std::move(undefinedSymFile);
+}
+
+void LinkingContext::createInternalFiles(
+ std::vector<std::unique_ptr<File>> &result) const {
+ if (std::unique_ptr<File> file = createEntrySymbolFile())
+ result.push_back(std::move(file));
+ if (std::unique_ptr<File> file = createUndefinedSymbolFile())
+ result.push_back(std::move(file));
+}
+
+} // end namespace lld
--- /dev/null
+//===- lib/Core/Reader.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Reader.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <algorithm>
+#include <memory>
+
+using llvm::file_magic;
+using llvm::identify_magic;
+
+namespace lld {
+
+YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() = default;
+
+void Registry::add(std::unique_ptr<Reader> reader) {
+ _readers.push_back(std::move(reader));
+}
+
+void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) {
+ _yamlHandlers.push_back(std::move(handler));
+}
+
+ErrorOr<std::unique_ptr<File>>
+Registry::loadFile(std::unique_ptr<MemoryBuffer> mb) const {
+ // Get file magic.
+ StringRef content(mb->getBufferStart(), mb->getBufferSize());
+ file_magic fileType = identify_magic(content);
+
+ // Ask each registered reader if it can handle this file type or extension.
+ for (const std::unique_ptr<Reader> &reader : _readers) {
+ if (!reader->canParse(fileType, mb->getMemBufferRef()))
+ continue;
+ return reader->loadFile(std::move(mb), *this);
+ }
+
+ // No Reader could parse this file.
+ return make_error_code(llvm::errc::executable_format_error);
+}
+
+static const Registry::KindStrings kindStrings[] = {
+ {Reference::kindLayoutAfter, "layout-after"},
+ {Reference::kindAssociate, "associate"},
+ LLD_KIND_STRING_END};
+
+Registry::Registry() {
+ addKindTable(Reference::KindNamespace::all, Reference::KindArch::all,
+ kindStrings);
+}
+
+bool Registry::handleTaggedDoc(llvm::yaml::IO &io,
+ const lld::File *&file) const {
+ for (const std::unique_ptr<YamlIOTaggedDocumentHandler> &h : _yamlHandlers)
+ if (h->handledDocTag(io, file))
+ return true;
+ return false;
+}
+
+void Registry::addKindTable(Reference::KindNamespace ns,
+ Reference::KindArch arch,
+ const KindStrings array[]) {
+ KindEntry entry = { ns, arch, array };
+ _kindEntries.push_back(entry);
+}
+
+bool Registry::referenceKindFromString(StringRef inputStr,
+ Reference::KindNamespace &ns,
+ Reference::KindArch &arch,
+ Reference::KindValue &value) const {
+ for (const KindEntry &entry : _kindEntries) {
+ for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
+ if (!inputStr.equals(pair->name))
+ continue;
+ ns = entry.ns;
+ arch = entry.arch;
+ value = pair->value;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Registry::referenceKindToString(Reference::KindNamespace ns,
+ Reference::KindArch arch,
+ Reference::KindValue value,
+ StringRef &str) const {
+ for (const KindEntry &entry : _kindEntries) {
+ if (entry.ns != ns)
+ continue;
+ if (entry.arch != arch)
+ continue;
+ for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
+ if (pair->value != value)
+ continue;
+ str = pair->name;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // end namespace lld
--- /dev/null
+//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Resolver.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/Atom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Instrumentation.h"
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "lld/Core/SymbolTable.h"
+#include "lld/Core/UndefinedAtom.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <utility>
+#include <vector>
+
+namespace lld {
+
+llvm::Expected<bool> Resolver::handleFile(File &file) {
+ if (auto ec = _ctx.handleLoadedFile(file))
+ return std::move(ec);
+ bool undefAdded = false;
+ for (auto &atom : file.defined().owning_ptrs())
+ doDefinedAtom(std::move(atom));
+ for (auto &atom : file.undefined().owning_ptrs()) {
+ if (doUndefinedAtom(std::move(atom)))
+ undefAdded = true;
+ }
+ for (auto &atom : file.sharedLibrary().owning_ptrs())
+ doSharedLibraryAtom(std::move(atom));
+ for (auto &atom : file.absolute().owning_ptrs())
+ doAbsoluteAtom(std::move(atom));
+ return undefAdded;
+}
+
+llvm::Expected<bool> Resolver::forEachUndefines(File &file,
+ UndefCallback callback) {
+ size_t i = _undefineIndex[&file];
+ bool undefAdded = false;
+ do {
+ for (; i < _undefines.size(); ++i) {
+ StringRef undefName = _undefines[i];
+ if (undefName.empty())
+ continue;
+ const Atom *atom = _symbolTable.findByName(undefName);
+ if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) {
+ // The symbol was resolved by some other file. Cache the result.
+ _undefines[i] = "";
+ continue;
+ }
+ auto undefAddedOrError = callback(undefName);
+ if (auto ec = undefAddedOrError.takeError())
+ return std::move(ec);
+ undefAdded |= undefAddedOrError.get();
+ }
+ } while (i < _undefines.size());
+ _undefineIndex[&file] = i;
+ return undefAdded;
+}
+
+llvm::Expected<bool> Resolver::handleArchiveFile(File &file) {
+ ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file);
+ return forEachUndefines(file,
+ [&](StringRef undefName) -> llvm::Expected<bool> {
+ if (File *member = archiveFile->find(undefName)) {
+ member->setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ return handleFile(*member);
+ }
+ return false;
+ });
+}
+
+llvm::Error Resolver::handleSharedLibrary(File &file) {
+ // Add all the atoms from the shared library
+ SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file);
+ auto undefAddedOrError = handleFile(*sharedLibrary);
+ if (auto ec = undefAddedOrError.takeError())
+ return ec;
+ undefAddedOrError =
+ forEachUndefines(file, [&](StringRef undefName) -> llvm::Expected<bool> {
+ auto atom = sharedLibrary->exports(undefName);
+ if (atom.get())
+ doSharedLibraryAtom(std::move(atom));
+ return false;
+ });
+
+ if (auto ec = undefAddedOrError.takeError())
+ return ec;
+ return llvm::Error::success();
+}
+
+bool Resolver::doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << " UndefinedAtom: "
+ << llvm::format("0x%09lX", atom.get())
+ << ", name=" << atom.get()->name() << "\n");
+
+ // tell symbol table
+ bool newUndefAdded = _symbolTable.add(*atom.get());
+ if (newUndefAdded)
+ _undefines.push_back(atom.get()->name());
+
+ // add to list of known atoms
+ _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
+
+ return newUndefAdded;
+}
+
+// Called on each atom when a file is added. Returns true if a given
+// atom is added to the symbol table.
+void Resolver::doDefinedAtom(OwningAtomPtr<DefinedAtom> atom) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << " DefinedAtom: "
+ << llvm::format("0x%09lX", atom.get())
+ << ", file=#"
+ << atom.get()->file().ordinal()
+ << ", atom=#"
+ << atom.get()->ordinal()
+ << ", name="
+ << atom.get()->name()
+ << ", type="
+ << atom.get()->contentType()
+ << "\n");
+
+ // An atom that should never be dead-stripped is a dead-strip root.
+ if (_ctx.deadStrip() &&
+ atom.get()->deadStrip() == DefinedAtom::deadStripNever) {
+ _deadStripRoots.insert(atom.get());
+ }
+
+ // add to list of known atoms
+ _symbolTable.add(*atom.get());
+ _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
+}
+
+void Resolver::doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << " SharedLibraryAtom: "
+ << llvm::format("0x%09lX", atom.get())
+ << ", name="
+ << atom.get()->name()
+ << "\n");
+
+ // tell symbol table
+ _symbolTable.add(*atom.get());
+
+ // add to list of known atoms
+ _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
+}
+
+void Resolver::doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << " AbsoluteAtom: "
+ << llvm::format("0x%09lX", atom.get())
+ << ", name="
+ << atom.get()->name()
+ << "\n");
+
+ // tell symbol table
+ if (atom.get()->scope() != Atom::scopeTranslationUnit)
+ _symbolTable.add(*atom.get());
+
+ // add to list of known atoms
+ _atoms.push_back(OwningAtomPtr<Atom>(atom.release()));
+}
+
+// Returns true if at least one of N previous files has created an
+// undefined symbol.
+bool Resolver::undefinesAdded(int begin, int end) {
+ std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
+ for (int i = begin; i < end; ++i)
+ if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
+ if (_newUndefinesAdded[node->getFile()])
+ return true;
+ return false;
+}
+
+File *Resolver::getFile(int &index) {
+ std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
+ if ((size_t)index >= inputs.size())
+ return nullptr;
+ if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
+ // We are at the end of the current group. If one or more new
+ // undefined atom has been added in the last groupSize files, we
+ // reiterate over the files.
+ int size = group->getSize();
+ if (undefinesAdded(index - size, index)) {
+ index -= size;
+ return getFile(index);
+ }
+ ++index;
+ return getFile(index);
+ }
+ return cast<FileNode>(inputs[index++].get())->getFile();
+}
+
+// Keep adding atoms until _ctx.getNextFile() returns an error. This
+// function is where undefined atoms are resolved.
+bool Resolver::resolveUndefines() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Resolving undefines:\n");
+ ScopedTask task(getDefaultDomain(), "resolveUndefines");
+ int index = 0;
+ std::set<File *> seen;
+ for (;;) {
+ bool undefAdded = false;
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "Loading file #" << index << "\n");
+ File *file = getFile(index);
+ if (!file)
+ return true;
+ if (std::error_code ec = file->parse()) {
+ llvm::errs() << "Cannot open " + file->path() << ": " << ec.message()
+ << "\n";
+ return false;
+ }
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "Loaded file: " << file->path() << "\n");
+ switch (file->kind()) {
+ case File::kindErrorObject:
+ case File::kindNormalizedObject:
+ case File::kindMachObject:
+ case File::kindCEntryObject:
+ case File::kindHeaderObject:
+ case File::kindEntryObject:
+ case File::kindUndefinedSymsObject:
+ case File::kindStubHelperObject:
+ case File::kindResolverMergedObject:
+ case File::kindSectCreateObject: {
+ // The same file may be visited more than once if the file is
+ // in --start-group and --end-group. Only library files should
+ // be processed more than once.
+ if (seen.count(file))
+ break;
+ seen.insert(file);
+ assert(!file->hasOrdinal());
+ file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ auto undefAddedOrError = handleFile(*file);
+ if (auto EC = undefAddedOrError.takeError()) {
+ // FIXME: This should be passed to logAllUnhandledErrors but it needs
+ // to be passed a Twine instead of a string.
+ llvm::errs() << "Error in " + file->path() << ": ";
+ logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
+ return false;
+ }
+ undefAdded = undefAddedOrError.get();
+ break;
+ }
+ case File::kindArchiveLibrary: {
+ if (!file->hasOrdinal())
+ file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ auto undefAddedOrError = handleArchiveFile(*file);
+ if (auto EC = undefAddedOrError.takeError()) {
+ // FIXME: This should be passed to logAllUnhandledErrors but it needs
+ // to be passed a Twine instead of a string.
+ llvm::errs() << "Error in " + file->path() << ": ";
+ logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
+ return false;
+ }
+ undefAdded = undefAddedOrError.get();
+ break;
+ }
+ case File::kindSharedLibrary:
+ if (!file->hasOrdinal())
+ file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ if (auto EC = handleSharedLibrary(*file)) {
+ // FIXME: This should be passed to logAllUnhandledErrors but it needs
+ // to be passed a Twine instead of a string.
+ llvm::errs() << "Error in " + file->path() << ": ";
+ logAllUnhandledErrors(std::move(EC), llvm::errs(), std::string());
+ return false;
+ }
+ break;
+ }
+ _newUndefinesAdded[file] = undefAdded;
+ }
+}
+
+// switch all references to undefined or coalesced away atoms
+// to the new defined atom
+void Resolver::updateReferences() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Updating references:\n");
+ ScopedTask task(getDefaultDomain(), "updateReferences");
+ for (const OwningAtomPtr<Atom> &atom : _atoms) {
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
+ for (const Reference *ref : *defAtom) {
+ // A reference of type kindAssociate shouldn't be updated.
+ // Instead, an atom having such reference will be removed
+ // if the target atom is coalesced away, so that they will
+ // go away as a group.
+ if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
+ ref->kindValue() == lld::Reference::kindAssociate) {
+ if (_symbolTable.isCoalescedAway(atom.get()))
+ _deadAtoms.insert(ref->target());
+ continue;
+ }
+ const Atom *newTarget = _symbolTable.replacement(ref->target());
+ const_cast<Reference *>(ref)->setTarget(newTarget);
+ }
+ }
+ }
+}
+
+// For dead code stripping, recursively mark atoms "live"
+void Resolver::markLive(const Atom *atom) {
+ // Mark the atom is live. If it's already marked live, then stop recursion.
+ auto exists = _liveAtoms.insert(atom);
+ if (!exists.second)
+ return;
+
+ // Mark all atoms it references as live
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
+ for (const Reference *ref : *defAtom)
+ markLive(ref->target());
+ for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) {
+ const Atom *target = p.second;
+ markLive(target);
+ }
+ }
+}
+
+static bool isBackref(const Reference *ref) {
+ if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
+ return false;
+ return (ref->kindValue() == lld::Reference::kindLayoutAfter);
+}
+
+// remove all atoms not actually used
+void Resolver::deadStripOptimize() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Dead stripping unused atoms:\n");
+ ScopedTask task(getDefaultDomain(), "deadStripOptimize");
+ // only do this optimization with -dead_strip
+ if (!_ctx.deadStrip())
+ return;
+
+ // Some type of references prevent referring atoms to be dead-striped.
+ // Make a reverse map of such references before traversing the graph.
+ // While traversing the list of atoms, mark AbsoluteAtoms as live
+ // in order to avoid reclaim.
+ for (const OwningAtomPtr<Atom> &atom : _atoms) {
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
+ for (const Reference *ref : *defAtom)
+ if (isBackref(ref))
+ _reverseRef.insert(std::make_pair(ref->target(), atom.get()));
+ if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom.get()))
+ markLive(absAtom);
+ }
+
+ // By default, shared libraries are built with all globals as dead strip roots
+ if (_ctx.globalsAreDeadStripRoots())
+ for (const OwningAtomPtr<Atom> &atom : _atoms)
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get()))
+ if (defAtom->scope() == DefinedAtom::scopeGlobal)
+ _deadStripRoots.insert(defAtom);
+
+ // Or, use list of names that are dead strip roots.
+ for (const StringRef &name : _ctx.deadStripRoots()) {
+ const Atom *symAtom = _symbolTable.findByName(name);
+ assert(symAtom);
+ _deadStripRoots.insert(symAtom);
+ }
+
+ // mark all roots as live, and recursively all atoms they reference
+ for (const Atom *dsrAtom : _deadStripRoots)
+ markLive(dsrAtom);
+
+ // now remove all non-live atoms from _atoms
+ llvm::erase_if(_atoms, [&](OwningAtomPtr<Atom> &a) {
+ return _liveAtoms.count(a.get()) == 0;
+ });
+}
+
+// error out if some undefines remain
+bool Resolver::checkUndefines() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Checking for undefines:\n");
+
+ // build vector of remaining undefined symbols
+ std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
+ if (_ctx.deadStrip()) {
+ // When dead code stripping, we don't care if dead atoms are undefined.
+ llvm::erase_if(undefinedAtoms,
+ [&](const Atom *a) { return _liveAtoms.count(a) == 0; });
+ }
+
+ if (undefinedAtoms.empty())
+ return false;
+
+ // Warn about unresolved symbols.
+ bool foundUndefines = false;
+ for (const UndefinedAtom *undef : undefinedAtoms) {
+ // Skip over a weak symbol.
+ if (undef->canBeNull() != UndefinedAtom::canBeNullNever)
+ continue;
+
+ // If this is a library and undefined symbols are allowed on the
+ // target platform, skip over it.
+ if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines())
+ continue;
+
+ // If the undefine is coalesced away, skip over it.
+ if (_symbolTable.isCoalescedAway(undef))
+ continue;
+
+ // Seems like this symbol is undefined. Warn that.
+ foundUndefines = true;
+ if (_ctx.printRemainingUndefines()) {
+ llvm::errs() << "Undefined symbol: " << undef->file().path() << ": "
+ << _ctx.demangle(undef->name()) << "\n";
+ }
+ }
+ if (!foundUndefines)
+ return false;
+ if (_ctx.printRemainingUndefines())
+ llvm::errs() << "symbol(s) not found\n";
+ return true;
+}
+
+// Remove from _atoms all coalesced away atoms.
+void Resolver::removeCoalescedAwayAtoms() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Removing coalesced away atoms:\n");
+ ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
+ llvm::erase_if(_atoms, [&](OwningAtomPtr<Atom> &a) {
+ return _symbolTable.isCoalescedAway(a.get()) || _deadAtoms.count(a.get());
+ });
+}
+
+bool Resolver::resolve() {
+ DEBUG_WITH_TYPE("resolver",
+ llvm::dbgs() << "******** Resolving atom references:\n");
+ if (!resolveUndefines())
+ return false;
+ updateReferences();
+ deadStripOptimize();
+ if (checkUndefines()) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... ");
+ if (!_ctx.allowRemainingUndefines()) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n");
+ return false;
+ }
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n");
+ }
+ removeCoalescedAwayAtoms();
+ _result->addAtoms(_atoms);
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n");
+ return true;
+}
+
+void Resolver::MergedFile::addAtoms(
+ llvm::MutableArrayRef<OwningAtomPtr<Atom>> all) {
+ ScopedTask task(getDefaultDomain(), "addAtoms");
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
+
+ for (OwningAtomPtr<Atom> &atom : all) {
+#ifndef NDEBUG
+ if (auto *definedAtom = dyn_cast<DefinedAtom>(atom.get())) {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << llvm::format(" 0x%09lX", definedAtom)
+ << ", file=#"
+ << definedAtom->file().ordinal()
+ << ", atom=#"
+ << definedAtom->ordinal()
+ << ", name="
+ << definedAtom->name()
+ << ", type="
+ << definedAtom->contentType()
+ << "\n");
+ } else {
+ DEBUG_WITH_TYPE("resolver", llvm::dbgs()
+ << llvm::format(" 0x%09lX", atom.get())
+ << ", name="
+ << atom.get()->name()
+ << "\n");
+ }
+#endif
+ addAtom(*atom.release());
+ }
+}
+
+} // namespace lld
--- /dev/null
+//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/SymbolTable.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/AbsoluteAtom.h"
+#include "lld/Core/Atom.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/Resolver.h"
+#include "lld/Core/SharedLibraryAtom.h"
+#include "lld/Core/UndefinedAtom.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <vector>
+
+namespace lld {
+bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); }
+
+bool SymbolTable::add(const SharedLibraryAtom &atom) { return addByName(atom); }
+
+bool SymbolTable::add(const AbsoluteAtom &atom) { return addByName(atom); }
+
+bool SymbolTable::add(const DefinedAtom &atom) {
+ if (!atom.name().empty() &&
+ atom.scope() != DefinedAtom::scopeTranslationUnit) {
+ // Named atoms cannot be merged by content.
+ assert(atom.merge() != DefinedAtom::mergeByContent);
+ // Track named atoms that are not scoped to file (static).
+ return addByName(atom);
+ }
+ if (atom.merge() == DefinedAtom::mergeByContent) {
+ // Named atoms cannot be merged by content.
+ assert(atom.name().empty());
+ // Currently only read-only constants can be merged.
+ if (atom.permissions() == DefinedAtom::permR__)
+ return addByContent(atom);
+ // TODO: support mergeByContent of data atoms by comparing content & fixups.
+ }
+ return false;
+}
+
+enum NameCollisionResolution {
+ NCR_First,
+ NCR_Second,
+ NCR_DupDef,
+ NCR_DupUndef,
+ NCR_DupShLib,
+ NCR_Error
+};
+
+static NameCollisionResolution cases[4][4] = {
+ //regular absolute undef sharedLib
+ {
+ // first is regular
+ NCR_DupDef, NCR_Error, NCR_First, NCR_First
+ },
+ {
+ // first is absolute
+ NCR_Error, NCR_Error, NCR_First, NCR_First
+ },
+ {
+ // first is undef
+ NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
+ },
+ {
+ // first is sharedLib
+ NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
+ }
+};
+
+static NameCollisionResolution collide(Atom::Definition first,
+ Atom::Definition second) {
+ return cases[first][second];
+}
+
+enum MergeResolution {
+ MCR_First,
+ MCR_Second,
+ MCR_Largest,
+ MCR_SameSize,
+ MCR_Error
+};
+
+static MergeResolution mergeCases[][6] = {
+ // no tentative weak weakAddress sameNameAndSize largest
+ {MCR_Error, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // no
+ {MCR_Second, MCR_Largest, MCR_Second, MCR_Second, MCR_SameSize, MCR_Largest}, // tentative
+ {MCR_Second, MCR_First, MCR_First, MCR_Second, MCR_SameSize, MCR_Largest}, // weak
+ {MCR_Second, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // weakAddress
+ {MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize
+ {MCR_Largest, MCR_Largest, MCR_Largest, MCR_Largest, MCR_SameSize, MCR_Largest}, // largest
+};
+
+static MergeResolution mergeSelect(DefinedAtom::Merge first,
+ DefinedAtom::Merge second) {
+ assert(first != DefinedAtom::mergeByContent);
+ assert(second != DefinedAtom::mergeByContent);
+ return mergeCases[first][second];
+}
+
+bool SymbolTable::addByName(const Atom &newAtom) {
+ StringRef name = newAtom.name();
+ assert(!name.empty());
+ const Atom *existing = findByName(name);
+ if (existing == nullptr) {
+ // Name is not in symbol table yet, add it associate with this atom.
+ _nameTable[name] = &newAtom;
+ return true;
+ }
+
+ // Do nothing if the same object is added more than once.
+ if (existing == &newAtom)
+ return false;
+
+ // Name is already in symbol table and associated with another atom.
+ bool useNew = true;
+ switch (collide(existing->definition(), newAtom.definition())) {
+ case NCR_First:
+ useNew = false;
+ break;
+ case NCR_Second:
+ useNew = true;
+ break;
+ case NCR_DupDef: {
+ const auto *existingDef = cast<DefinedAtom>(existing);
+ const auto *newDef = cast<DefinedAtom>(&newAtom);
+ switch (mergeSelect(existingDef->merge(), newDef->merge())) {
+ case MCR_First:
+ useNew = false;
+ break;
+ case MCR_Second:
+ useNew = true;
+ break;
+ case MCR_Largest: {
+ uint64_t existingSize = existingDef->sectionSize();
+ uint64_t newSize = newDef->sectionSize();
+ useNew = (newSize >= existingSize);
+ break;
+ }
+ case MCR_SameSize: {
+ uint64_t existingSize = existingDef->sectionSize();
+ uint64_t newSize = newDef->sectionSize();
+ if (existingSize == newSize) {
+ useNew = true;
+ break;
+ }
+ llvm::errs() << "Size mismatch: " << existing->name() << " ("
+ << existingSize << ") " << newAtom.name() << " (" << newSize
+ << ")\n";
+ LLVM_FALLTHROUGH;
+ }
+ case MCR_Error:
+ llvm::errs() << "Duplicate symbols: " << existing->name() << ":"
+ << existing->file().path() << " and " << newAtom.name()
+ << ":" << newAtom.file().path() << "\n";
+ llvm::report_fatal_error("duplicate symbol error");
+ break;
+ }
+ break;
+ }
+ case NCR_DupUndef: {
+ const UndefinedAtom* existingUndef = cast<UndefinedAtom>(existing);
+ const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom);
+
+ bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
+ if (sameCanBeNull)
+ useNew = false;
+ else
+ useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
+ break;
+ }
+ case NCR_DupShLib: {
+ useNew = false;
+ break;
+ }
+ case NCR_Error:
+ llvm::errs() << "SymbolTable: error while merging " << name << "\n";
+ llvm::report_fatal_error("duplicate symbol error");
+ break;
+ }
+
+ if (useNew) {
+ // Update name table to use new atom.
+ _nameTable[name] = &newAtom;
+ // Add existing atom to replacement table.
+ _replacedAtoms[existing] = &newAtom;
+ } else {
+ // New atom is not being used. Add it to replacement table.
+ _replacedAtoms[&newAtom] = existing;
+ }
+ return false;
+}
+
+unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
+ auto content = atom->rawContent();
+ return llvm::hash_combine(atom->size(),
+ atom->contentType(),
+ llvm::hash_combine_range(content.begin(),
+ content.end()));
+}
+
+bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
+ const DefinedAtom * const r) {
+ if (l == r)
+ return true;
+ if (l == getEmptyKey() || r == getEmptyKey())
+ return false;
+ if (l == getTombstoneKey() || r == getTombstoneKey())
+ return false;
+ if (l->contentType() != r->contentType())
+ return false;
+ if (l->size() != r->size())
+ return false;
+ if (l->sectionChoice() != r->sectionChoice())
+ return false;
+ if (l->sectionChoice() == DefinedAtom::sectionCustomRequired) {
+ if (!l->customSectionName().equals(r->customSectionName()))
+ return false;
+ }
+ ArrayRef<uint8_t> lc = l->rawContent();
+ ArrayRef<uint8_t> rc = r->rawContent();
+ return memcmp(lc.data(), rc.data(), lc.size()) == 0;
+}
+
+bool SymbolTable::addByContent(const DefinedAtom &newAtom) {
+ AtomContentSet::iterator pos = _contentTable.find(&newAtom);
+ if (pos == _contentTable.end()) {
+ _contentTable.insert(&newAtom);
+ return true;
+ }
+ const Atom* existing = *pos;
+ // New atom is not being used. Add it to replacement table.
+ _replacedAtoms[&newAtom] = existing;
+ return false;
+}
+
+const Atom *SymbolTable::findByName(StringRef sym) {
+ NameToAtom::iterator pos = _nameTable.find(sym);
+ if (pos == _nameTable.end())
+ return nullptr;
+ return pos->second;
+}
+
+const Atom *SymbolTable::replacement(const Atom *atom) {
+ // Find the replacement for a given atom. Atoms in _replacedAtoms
+ // may be chained, so find the last one.
+ for (;;) {
+ AtomToAtom::iterator pos = _replacedAtoms.find(atom);
+ if (pos == _replacedAtoms.end())
+ return atom;
+ atom = pos->second;
+ }
+}
+
+bool SymbolTable::isCoalescedAway(const Atom *atom) {
+ return _replacedAtoms.count(atom) > 0;
+}
+
+std::vector<const UndefinedAtom *> SymbolTable::undefines() {
+ std::vector<const UndefinedAtom *> ret;
+ for (auto it : _nameTable) {
+ const Atom *atom = it.second;
+ assert(atom != nullptr);
+ if (const auto *undef = dyn_cast<const UndefinedAtom>(atom))
+ if (_replacedAtoms.count(undef) == 0)
+ ret.push_back(undef);
+ }
+ return ret;
+}
+
+} // namespace lld
--- /dev/null
+//===- lib/Core/Writer.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Writer.h"
+
+namespace lld {
+
+Writer::Writer() = default;
+
+Writer::~Writer() = default;
+
+} // end namespace lld
--- /dev/null
+set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
+tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
+add_public_tablegen_target(DriverOptionsTableGen)
+
+add_lld_library(lldDriver
+ DarwinLdDriver.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${LLD_INCLUDE_DIR}/lld/Driver
+
+ LINK_COMPONENTS
+ Option
+ Support
+
+ LINK_LIBS
+ lldCommon
+ lldCore
+ lldMachOOld
+ lldReaderWriter
+ lldYAML
+ )
+
+add_dependencies(lldDriver DriverOptionsTableGen)
--- /dev/null
+//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Concrete instance of the Driver for darwin's ld.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Args.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/Error.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Instrumentation.h"
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/Node.h"
+#include "lld/Core/PassManager.h"
+#include "lld/Core/Resolver.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+using namespace lld;
+
+namespace {
+
+// Create enum with OPT_xxx values for each option in DarwinLdOptions.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META, VALUES) \
+ OPT_##ID,
+#include "DarwinLdOptions.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in DarwinLdOptions.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "DarwinLdOptions.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in DarwinLdOptions.td
+static const llvm::opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "DarwinLdOptions.inc"
+#undef OPTION
+};
+
+// Create OptTable class for parsing actual command line arguments
+class DarwinLdOptTable : public llvm::opt::OptTable {
+public:
+ DarwinLdOptTable() : OptTable(InfoTable) {}
+};
+
+static std::vector<std::unique_ptr<File>>
+makeErrorFile(StringRef path, std::error_code ec) {
+ std::vector<std::unique_ptr<File>> result;
+ result.push_back(std::make_unique<ErrorFile>(path, ec));
+ return result;
+}
+
+static std::vector<std::unique_ptr<File>>
+parseMemberFiles(std::unique_ptr<File> file) {
+ std::vector<std::unique_ptr<File>> members;
+ if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) {
+ if (std::error_code ec = archive->parseAllMembers(members))
+ return makeErrorFile(file->path(), ec);
+ } else {
+ members.push_back(std::move(file));
+ }
+ return members;
+}
+
+std::vector<std::unique_ptr<File>> loadFile(MachOLinkingContext &ctx,
+ StringRef path, bool wholeArchive,
+ bool upwardDylib) {
+ if (ctx.logInputFiles())
+ message(path);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path);
+ if (std::error_code ec = mbOrErr.getError())
+ return makeErrorFile(path, ec);
+ ErrorOr<std::unique_ptr<File>> fileOrErr =
+ ctx.registry().loadFile(std::move(mbOrErr.get()));
+ if (std::error_code ec = fileOrErr.getError())
+ return makeErrorFile(path, ec);
+ std::unique_ptr<File> &file = fileOrErr.get();
+
+ // If file is a dylib, inform LinkingContext about it.
+ if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(file.get())) {
+ if (std::error_code ec = shl->parse())
+ return makeErrorFile(path, ec);
+ ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile *>(shl),
+ upwardDylib);
+ }
+ if (wholeArchive)
+ return parseMemberFiles(std::move(file));
+ std::vector<std::unique_ptr<File>> files;
+ files.push_back(std::move(file));
+ return files;
+}
+
+} // end anonymous namespace
+
+// Test may be running on Windows. Canonicalize the path
+// separator to '/' to get consistent outputs for tests.
+static std::string canonicalizePath(StringRef path) {
+ char sep = llvm::sys::path::get_separator().front();
+ if (sep != '/') {
+ std::string fixedPath = std::string(path);
+ std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
+ return fixedPath;
+ } else {
+ return std::string(path);
+ }
+}
+
+static void addFile(StringRef path, MachOLinkingContext &ctx,
+ bool loadWholeArchive, bool upwardDylib) {
+ std::vector<std::unique_ptr<File>> files =
+ loadFile(ctx, path, loadWholeArchive, upwardDylib);
+ for (std::unique_ptr<File> &file : files)
+ ctx.getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
+}
+
+// Export lists are one symbol per line. Blank lines are ignored.
+// Trailing comments start with #.
+static std::error_code parseExportsList(StringRef exportFilePath,
+ MachOLinkingContext &ctx) {
+ // Map in export list file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(exportFilePath);
+ if (std::error_code ec = mb.getError())
+ return ec;
+ ctx.addInputFileDependency(exportFilePath);
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ // Ignore trailing # comments.
+ std::pair<StringRef, StringRef> symAndComment = line.split('#');
+ StringRef sym = symAndComment.first.trim();
+ if (!sym.empty())
+ ctx.addExportSymbol(sym);
+ buffer = lineAndRest.second;
+ }
+ return std::error_code();
+}
+
+/// Order files are one symbol per line. Blank lines are ignored.
+/// Trailing comments start with #. Symbol names can be prefixed with an
+/// architecture name and/or .o leaf name. Examples:
+/// _foo
+/// bar.o:_bar
+/// libfrob.a(bar.o):_bar
+/// x86_64:_foo64
+static std::error_code parseOrderFile(StringRef orderFilePath,
+ MachOLinkingContext &ctx) {
+ // Map in order file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(orderFilePath);
+ if (std::error_code ec = mb.getError())
+ return ec;
+ ctx.addInputFileDependency(orderFilePath);
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ buffer = lineAndRest.second;
+ // Ignore trailing # comments.
+ std::pair<StringRef, StringRef> symAndComment = line.split('#');
+ if (symAndComment.first.empty())
+ continue;
+ StringRef sym = symAndComment.first.trim();
+ if (sym.empty())
+ continue;
+ // Check for prefix.
+ StringRef prefix;
+ std::pair<StringRef, StringRef> prefixAndSym = sym.split(':');
+ if (!prefixAndSym.second.empty()) {
+ sym = prefixAndSym.second;
+ prefix = prefixAndSym.first;
+ if (!prefix.endswith(".o") && !prefix.endswith(".o)")) {
+ // If arch name prefix does not match arch being linked, ignore symbol.
+ if (!ctx.archName().equals(prefix))
+ continue;
+ prefix = "";
+ }
+ } else
+ sym = prefixAndSym.first;
+ if (!sym.empty()) {
+ ctx.appendOrderedSymbol(sym, prefix);
+ // llvm::errs() << sym << ", prefix=" << prefix << "\n";
+ }
+ }
+ return std::error_code();
+}
+
+//
+// There are two variants of the -filelist option:
+//
+// -filelist <path>
+// In this variant, the path is to a text file which contains one file path
+// per line. There are no comments or trimming of whitespace.
+//
+// -fileList <path>,<dir>
+// In this variant, the path is to a text file which contains a partial path
+// per line. The <dir> prefix is prepended to each partial path.
+//
+static llvm::Error loadFileList(StringRef fileListPath,
+ MachOLinkingContext &ctx, bool forceLoad) {
+ // If there is a comma, split off <dir>.
+ std::pair<StringRef, StringRef> opt = fileListPath.split(',');
+ StringRef filePath = opt.first;
+ StringRef dirName = opt.second;
+ ctx.addInputFileDependency(filePath);
+ // Map in file list file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(filePath);
+ if (std::error_code ec = mb.getError())
+ return llvm::errorCodeToError(ec);
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ StringRef path;
+ if (!dirName.empty()) {
+ // If there is a <dir> then prepend dir to each line.
+ SmallString<256> fullPath;
+ fullPath.assign(dirName);
+ llvm::sys::path::append(fullPath, Twine(line));
+ path = ctx.copy(fullPath.str());
+ } else {
+ // No <dir> use whole line as input file path.
+ path = ctx.copy(line);
+ }
+ if (!ctx.pathExists(path)) {
+ return llvm::make_error<GenericError>(Twine("File not found '")
+ + path
+ + "'");
+ }
+ if (ctx.testingFileUsage()) {
+ message("Found filelist entry " + canonicalizePath(path));
+ }
+ addFile(path, ctx, forceLoad, false);
+ buffer = lineAndRest.second;
+ }
+ return llvm::Error::success();
+}
+
+/// Parse number assuming it is base 16, but allow 0x prefix.
+static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) {
+ if (numStr.startswith_insensitive("0x"))
+ numStr = numStr.drop_front(2);
+ return numStr.getAsInteger(16, baseAddress);
+}
+
+static void parseLLVMOptions(const LinkingContext &ctx) {
+ // Honor -mllvm
+ if (!ctx.llvmOptions().empty()) {
+ unsigned numArgs = ctx.llvmOptions().size();
+ auto **args = new const char *[numArgs + 2];
+ args[0] = "lld (LLVM option parsing)";
+ for (unsigned i = 0; i != numArgs; ++i)
+ args[i + 1] = ctx.llvmOptions()[i];
+ args[numArgs + 1] = nullptr;
+ llvm::cl::ResetAllOptionOccurrences();
+ llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
+ }
+}
+
+namespace lld {
+namespace mach_o {
+
+bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx) {
+ // Parse command line options using DarwinLdOptions.td
+ DarwinLdOptTable table;
+ unsigned missingIndex;
+ unsigned missingCount;
+ llvm::opt::InputArgList parsedArgs =
+ table.ParseArgs(args.slice(1), missingIndex, missingCount);
+ if (missingCount) {
+ error("missing arg value for '" +
+ Twine(parsedArgs.getArgString(missingIndex)) + "' expected " +
+ Twine(missingCount) + " argument(s).");
+ return false;
+ }
+
+ for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) {
+ warn("ignoring unknown argument: " +
+ Twine(unknownArg->getAsString(parsedArgs)));
+ }
+
+ errorHandler().verbose = parsedArgs.hasArg(OPT_v);
+ errorHandler().errorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20);
+
+ // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static )
+ llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE;
+ bool isStaticExecutable = false;
+ if (llvm::opt::Arg *kind = parsedArgs.getLastArg(
+ OPT_dylib, OPT_relocatable, OPT_bundle, OPT_static, OPT_preload)) {
+ switch (kind->getOption().getID()) {
+ case OPT_dylib:
+ fileType = llvm::MachO::MH_DYLIB;
+ break;
+ case OPT_relocatable:
+ fileType = llvm::MachO::MH_OBJECT;
+ break;
+ case OPT_bundle:
+ fileType = llvm::MachO::MH_BUNDLE;
+ break;
+ case OPT_static:
+ fileType = llvm::MachO::MH_EXECUTE;
+ isStaticExecutable = true;
+ break;
+ case OPT_preload:
+ fileType = llvm::MachO::MH_PRELOAD;
+ break;
+ }
+ }
+
+ // Handle -arch xxx
+ MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
+ if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) {
+ arch = MachOLinkingContext::archFromName(archStr->getValue());
+ if (arch == MachOLinkingContext::arch_unknown) {
+ error("unknown arch named '" + Twine(archStr->getValue()) + "'");
+ return false;
+ }
+ }
+ // If no -arch specified, scan input files to find first non-fat .o file.
+ if (arch == MachOLinkingContext::arch_unknown) {
+ for (auto &inFile : parsedArgs.filtered(OPT_INPUT)) {
+ // This is expensive because it opens and maps the file. But that is
+ // ok because no -arch is rare.
+ if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch))
+ break;
+ }
+ if (arch == MachOLinkingContext::arch_unknown &&
+ !parsedArgs.getLastArg(OPT_test_file_usage)) {
+ // If no -arch and no options at all, print usage message.
+ if (parsedArgs.size() == 0) {
+ table.printHelp(llvm::outs(),
+ (std::string(args[0]) + " [options] file...").c_str(),
+ "LLVM Linker", false);
+ } else {
+ error("-arch not specified and could not be inferred");
+ }
+ return false;
+ }
+ }
+
+ // Handle -macosx_version_min or -ios_version_min
+ MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown;
+ uint32_t minOSVersion = 0;
+ if (llvm::opt::Arg *minOS =
+ parsedArgs.getLastArg(OPT_macosx_version_min, OPT_ios_version_min,
+ OPT_ios_simulator_version_min)) {
+ switch (minOS->getOption().getID()) {
+ case OPT_macosx_version_min:
+ os = MachOLinkingContext::OS::macOSX;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ error("malformed macosx_version_min value");
+ return false;
+ }
+ break;
+ case OPT_ios_version_min:
+ os = MachOLinkingContext::OS::iOS;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ error("malformed ios_version_min value");
+ return false;
+ }
+ break;
+ case OPT_ios_simulator_version_min:
+ os = MachOLinkingContext::OS::iOS_simulator;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ error("malformed ios_simulator_version_min value");
+ return false;
+ }
+ break;
+ }
+ } else {
+ // No min-os version on command line, check environment variables
+ }
+
+ // Handle export_dynamic
+ // FIXME: Should we warn when this applies to something other than a static
+ // executable or dylib? Those are the only cases where this has an effect.
+ // Note, this has to come before ctx.configure() so that we get the correct
+ // value for _globalsAreDeadStripRoots.
+ bool exportDynamicSymbols = parsedArgs.hasArg(OPT_export_dynamic);
+
+ // Now that there's enough information parsed in, let the linking context
+ // set up default values.
+ ctx.configure(fileType, arch, os, minOSVersion, exportDynamicSymbols);
+
+ // Handle -e xxx
+ if (llvm::opt::Arg *entry = parsedArgs.getLastArg(OPT_entry))
+ ctx.setEntrySymbolName(entry->getValue());
+
+ // Handle -o xxx
+ if (llvm::opt::Arg *outpath = parsedArgs.getLastArg(OPT_output))
+ ctx.setOutputPath(outpath->getValue());
+ else
+ ctx.setOutputPath("a.out");
+
+ // Handle -image_base XXX and -seg1addr XXXX
+ if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) {
+ uint64_t baseAddress;
+ if (parseNumberBase16(imageBase->getValue(), baseAddress)) {
+ error("image_base expects a hex number");
+ return false;
+ } else if (baseAddress < ctx.pageZeroSize()) {
+ error("image_base overlaps with __PAGEZERO");
+ return false;
+ } else if (baseAddress % ctx.pageSize()) {
+ error("image_base must be a multiple of page size (0x" +
+ llvm::utohexstr(ctx.pageSize()) + ")");
+ return false;
+ }
+
+ ctx.setBaseAddress(baseAddress);
+ }
+
+ // Handle -dead_strip
+ if (parsedArgs.getLastArg(OPT_dead_strip))
+ ctx.setDeadStripping(true);
+
+ bool globalWholeArchive = false;
+ // Handle -all_load
+ if (parsedArgs.getLastArg(OPT_all_load))
+ globalWholeArchive = true;
+
+ // Handle -install_name
+ if (llvm::opt::Arg *installName = parsedArgs.getLastArg(OPT_install_name))
+ ctx.setInstallName(installName->getValue());
+ else
+ ctx.setInstallName(ctx.outputPath());
+
+ // Handle -mark_dead_strippable_dylib
+ if (parsedArgs.getLastArg(OPT_mark_dead_strippable_dylib))
+ ctx.setDeadStrippableDylib(true);
+
+ // Handle -compatibility_version and -current_version
+ if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) {
+ if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
+ error("-compatibility_version can only be used with -dylib");
+ return false;
+ }
+ uint32_t parsedVers;
+ if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
+ error("-compatibility_version value is malformed");
+ return false;
+ }
+ ctx.setCompatibilityVersion(parsedVers);
+ }
+
+ if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) {
+ if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
+ error("-current_version can only be used with -dylib");
+ return false;
+ }
+ uint32_t parsedVers;
+ if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
+ error("-current_version value is malformed");
+ return false;
+ }
+ ctx.setCurrentVersion(parsedVers);
+ }
+
+ // Handle -bundle_loader
+ if (llvm::opt::Arg *loader = parsedArgs.getLastArg(OPT_bundle_loader))
+ ctx.setBundleLoader(loader->getValue());
+
+ // Handle -sectalign segname sectname align
+ for (auto &alignArg : parsedArgs.filtered(OPT_sectalign)) {
+ const char* segName = alignArg->getValue(0);
+ const char* sectName = alignArg->getValue(1);
+ const char* alignStr = alignArg->getValue(2);
+ if ((alignStr[0] == '0') && (alignStr[1] == 'x'))
+ alignStr += 2;
+ unsigned long long alignValue;
+ if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) {
+ error("-sectalign alignment value '" + Twine(alignStr) +
+ "' not a valid number");
+ return false;
+ }
+ uint16_t align = 1 << llvm::countTrailingZeros(alignValue);
+ if (!llvm::isPowerOf2_64(alignValue)) {
+ std::string Msg;
+ llvm::raw_string_ostream OS(Msg);
+ OS << "alignment for '-sectalign " << segName << " " << sectName
+ << llvm::format(" 0x%llX", alignValue)
+ << "' is not a power of two, using " << llvm::format("0x%08X", align);
+ OS.flush();
+ warn(Msg);
+ }
+ ctx.addSectionAlignment(segName, sectName, align);
+ }
+
+ // Handle -mllvm
+ for (auto &llvmArg : parsedArgs.filtered(OPT_mllvm)) {
+ ctx.appendLLVMOption(llvmArg->getValue());
+ }
+
+ // Handle -print_atoms
+ if (parsedArgs.getLastArg(OPT_print_atoms))
+ ctx.setPrintAtoms();
+
+ // Handle -t (trace) option.
+ if (parsedArgs.getLastArg(OPT_t))
+ ctx.setLogInputFiles(true);
+
+ // Handle -demangle option.
+ if (parsedArgs.getLastArg(OPT_demangle))
+ ctx.setDemangleSymbols(true);
+
+ // Handle -keep_private_externs
+ if (parsedArgs.getLastArg(OPT_keep_private_externs)) {
+ ctx.setKeepPrivateExterns(true);
+ if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
+ warn("-keep_private_externs only used in -r mode");
+ }
+
+ // Handle -dependency_info <path> used by Xcode.
+ if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info))
+ if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue()))
+ warn(ec.message() + ", processing '-dependency_info " +
+ depInfo->getValue());
+
+ // In -test_file_usage mode, we'll be given an explicit list of paths that
+ // exist. We'll also be expected to print out information about how we located
+ // libraries and so on that the user specified, but not to actually do any
+ // linking.
+ if (parsedArgs.getLastArg(OPT_test_file_usage)) {
+ ctx.setTestingFileUsage();
+
+ // With paths existing by fiat, linking is not going to end well.
+ ctx.setDoNothing(true);
+
+ // Only bother looking for an existence override if we're going to use it.
+ for (auto existingPath : parsedArgs.filtered(OPT_path_exists)) {
+ ctx.addExistingPathForDebug(existingPath->getValue());
+ }
+ }
+
+ // Register possible input file parsers.
+ if (!ctx.doNothing()) {
+ ctx.registry().addSupportMachOObjects(ctx);
+ ctx.registry().addSupportArchives(ctx.logInputFiles());
+ ctx.registry().addSupportYamlFiles();
+ }
+
+ // Now construct the set of library search directories, following ld64's
+ // baroque set of accumulated hacks. Mostly, the algorithm constructs
+ // { syslibroots } x { libpaths }
+ //
+ // Unfortunately, there are numerous exceptions:
+ // 1. Only absolute paths get modified by syslibroot options.
+ // 2. If there is just 1 -syslibroot, system paths not found in it are
+ // skipped.
+ // 3. If the last -syslibroot is "/", all of them are ignored entirely.
+ // 4. If { syslibroots } x path == {}, the original path is kept.
+ std::vector<StringRef> sysLibRoots;
+ for (auto syslibRoot : parsedArgs.filtered(OPT_syslibroot)) {
+ sysLibRoots.push_back(syslibRoot->getValue());
+ }
+ if (!sysLibRoots.empty()) {
+ // Ignore all if last -syslibroot is "/".
+ if (sysLibRoots.back() != "/")
+ ctx.setSysLibRoots(sysLibRoots);
+ }
+
+ // Paths specified with -L come first, and are not considered system paths for
+ // the case where there is precisely 1 -syslibroot.
+ for (auto libPath : parsedArgs.filtered(OPT_L)) {
+ ctx.addModifiedSearchDir(libPath->getValue());
+ }
+
+ // Process -F directories (where to look for frameworks).
+ for (auto fwPath : parsedArgs.filtered(OPT_F)) {
+ ctx.addFrameworkSearchDir(fwPath->getValue());
+ }
+
+ // -Z suppresses the standard search paths.
+ if (!parsedArgs.hasArg(OPT_Z)) {
+ ctx.addModifiedSearchDir("/usr/lib", true);
+ ctx.addModifiedSearchDir("/usr/local/lib", true);
+ ctx.addFrameworkSearchDir("/Library/Frameworks", true);
+ ctx.addFrameworkSearchDir("/System/Library/Frameworks", true);
+ }
+
+ // Now that we've constructed the final set of search paths, print out those
+ // search paths in verbose mode.
+ if (errorHandler().verbose) {
+ message("Library search paths:");
+ for (auto path : ctx.searchDirs()) {
+ message(" " + path);
+ }
+ message("Framework search paths:");
+ for (auto path : ctx.frameworkDirs()) {
+ message(" " + path);
+ }
+ }
+
+ // Handle -exported_symbols_list <file>
+ for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
+ error("-exported_symbols_list cannot be combined with "
+ "-unexported_symbol[s_list]");
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
+ if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
+ error(ec.message() + ", processing '-exported_symbols_list " +
+ expFile->getValue());
+ return false;
+ }
+ }
+
+ // Handle -exported_symbol <symbol>
+ for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
+ error("-exported_symbol cannot be combined with "
+ "-unexported_symbol[s_list]");
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
+ ctx.addExportSymbol(symbol->getValue());
+ }
+
+ // Handle -unexported_symbols_list <file>
+ for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
+ error("-unexported_symbols_list cannot be combined with "
+ "-exported_symbol[s_list]");
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
+ if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
+ error(ec.message() + ", processing '-unexported_symbols_list " +
+ expFile->getValue());
+ return false;
+ }
+ }
+
+ // Handle -unexported_symbol <symbol>
+ for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
+ error("-unexported_symbol cannot be combined with "
+ "-exported_symbol[s_list]");
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
+ ctx.addExportSymbol(symbol->getValue());
+ }
+
+ // Handle obosolete -multi_module and -single_module
+ if (llvm::opt::Arg *mod =
+ parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) {
+ if (mod->getOption().getID() == OPT_multi_module)
+ warn("-multi_module is obsolete and being ignored");
+ else if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB)
+ warn("-single_module being ignored. It is only for use when producing a "
+ "dylib");
+ }
+
+ // Handle obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only
+ if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) {
+ error("-objc_gc_compaction is not supported");
+ return false;
+ }
+
+ if (parsedArgs.getLastArg(OPT_objc_gc)) {
+ error("-objc_gc is not supported");
+ return false;
+ }
+
+ if (parsedArgs.getLastArg(OPT_objc_gc_only)) {
+ error("-objc_gc_only is not supported");
+ return false;
+ }
+
+ // Handle -pie or -no_pie
+ if (llvm::opt::Arg *pie = parsedArgs.getLastArg(OPT_pie, OPT_no_pie)) {
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_EXECUTE:
+ switch (ctx.os()) {
+ case MachOLinkingContext::OS::macOSX:
+ if ((minOSVersion < 0x000A0500) &&
+ (pie->getOption().getID() == OPT_pie)) {
+ error("-pie can only be used when targeting Mac OS X 10.5 or later");
+ return false;
+ }
+ break;
+ case MachOLinkingContext::OS::iOS:
+ if ((minOSVersion < 0x00040200) &&
+ (pie->getOption().getID() == OPT_pie)) {
+ error("-pie can only be used when targeting iOS 4.2 or later");
+ return false;
+ }
+ break;
+ case MachOLinkingContext::OS::iOS_simulator:
+ if (pie->getOption().getID() == OPT_no_pie) {
+ error("iOS simulator programs must be built PIE");
+ return false;
+ }
+ break;
+ case MachOLinkingContext::OS::unknown:
+ break;
+ }
+ ctx.setPIE(pie->getOption().getID() == OPT_pie);
+ break;
+ case llvm::MachO::MH_PRELOAD:
+ break;
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ warn(pie->getSpelling() +
+ " being ignored. It is only used when linking main executables");
+ break;
+ default:
+ error(pie->getSpelling() +
+ " can only used when linking main executables");
+ return false;
+ }
+ }
+
+ // Handle -version_load_command or -no_version_load_command
+ {
+ bool flagOn = false;
+ bool flagOff = false;
+ if (auto *arg = parsedArgs.getLastArg(OPT_version_load_command,
+ OPT_no_version_load_command)) {
+ flagOn = arg->getOption().getID() == OPT_version_load_command;
+ flagOff = arg->getOption().getID() == OPT_no_version_load_command;
+ }
+
+ // default to adding version load command for dynamic code,
+ // static code must opt-in
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_OBJECT:
+ ctx.setGenerateVersionLoadCommand(false);
+ break;
+ case llvm::MachO::MH_EXECUTE:
+ // dynamic executables default to generating a version load command,
+ // while static executables only generate it if required.
+ if (isStaticExecutable) {
+ if (flagOn)
+ ctx.setGenerateVersionLoadCommand(true);
+ } else {
+ if (!flagOff)
+ ctx.setGenerateVersionLoadCommand(true);
+ }
+ break;
+ case llvm::MachO::MH_PRELOAD:
+ case llvm::MachO::MH_KEXT_BUNDLE:
+ if (flagOn)
+ ctx.setGenerateVersionLoadCommand(true);
+ break;
+ case llvm::MachO::MH_DYLINKER:
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ if (!flagOff)
+ ctx.setGenerateVersionLoadCommand(true);
+ break;
+ case llvm::MachO::MH_FVMLIB:
+ case llvm::MachO::MH_DYLDLINK:
+ case llvm::MachO::MH_DYLIB_STUB:
+ case llvm::MachO::MH_DSYM:
+ // We don't generate load commands for these file types, even if
+ // forced on.
+ break;
+ }
+ }
+
+ // Handle -function_starts or -no_function_starts
+ {
+ bool flagOn = false;
+ bool flagOff = false;
+ if (auto *arg = parsedArgs.getLastArg(OPT_function_starts,
+ OPT_no_function_starts)) {
+ flagOn = arg->getOption().getID() == OPT_function_starts;
+ flagOff = arg->getOption().getID() == OPT_no_function_starts;
+ }
+
+ // default to adding functions start for dynamic code, static code must
+ // opt-in
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_OBJECT:
+ ctx.setGenerateFunctionStartsLoadCommand(false);
+ break;
+ case llvm::MachO::MH_EXECUTE:
+ // dynamic executables default to generating a version load command,
+ // while static executables only generate it if required.
+ if (isStaticExecutable) {
+ if (flagOn)
+ ctx.setGenerateFunctionStartsLoadCommand(true);
+ } else {
+ if (!flagOff)
+ ctx.setGenerateFunctionStartsLoadCommand(true);
+ }
+ break;
+ case llvm::MachO::MH_PRELOAD:
+ case llvm::MachO::MH_KEXT_BUNDLE:
+ if (flagOn)
+ ctx.setGenerateFunctionStartsLoadCommand(true);
+ break;
+ case llvm::MachO::MH_DYLINKER:
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ if (!flagOff)
+ ctx.setGenerateFunctionStartsLoadCommand(true);
+ break;
+ case llvm::MachO::MH_FVMLIB:
+ case llvm::MachO::MH_DYLDLINK:
+ case llvm::MachO::MH_DYLIB_STUB:
+ case llvm::MachO::MH_DSYM:
+ // We don't generate load commands for these file types, even if
+ // forced on.
+ break;
+ }
+ }
+
+ // Handle -data_in_code_info or -no_data_in_code_info
+ {
+ bool flagOn = false;
+ bool flagOff = false;
+ if (auto *arg = parsedArgs.getLastArg(OPT_data_in_code_info,
+ OPT_no_data_in_code_info)) {
+ flagOn = arg->getOption().getID() == OPT_data_in_code_info;
+ flagOff = arg->getOption().getID() == OPT_no_data_in_code_info;
+ }
+
+ // default to adding data in code for dynamic code, static code must
+ // opt-in
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_OBJECT:
+ if (!flagOff)
+ ctx.setGenerateDataInCodeLoadCommand(true);
+ break;
+ case llvm::MachO::MH_EXECUTE:
+ // dynamic executables default to generating a version load command,
+ // while static executables only generate it if required.
+ if (isStaticExecutable) {
+ if (flagOn)
+ ctx.setGenerateDataInCodeLoadCommand(true);
+ } else {
+ if (!flagOff)
+ ctx.setGenerateDataInCodeLoadCommand(true);
+ }
+ break;
+ case llvm::MachO::MH_PRELOAD:
+ case llvm::MachO::MH_KEXT_BUNDLE:
+ if (flagOn)
+ ctx.setGenerateDataInCodeLoadCommand(true);
+ break;
+ case llvm::MachO::MH_DYLINKER:
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ if (!flagOff)
+ ctx.setGenerateDataInCodeLoadCommand(true);
+ break;
+ case llvm::MachO::MH_FVMLIB:
+ case llvm::MachO::MH_DYLDLINK:
+ case llvm::MachO::MH_DYLIB_STUB:
+ case llvm::MachO::MH_DSYM:
+ // We don't generate load commands for these file types, even if
+ // forced on.
+ break;
+ }
+ }
+
+ // Handle sdk_version
+ if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_sdk_version)) {
+ uint32_t sdkVersion = 0;
+ if (MachOLinkingContext::parsePackedVersion(arg->getValue(),
+ sdkVersion)) {
+ error("malformed sdkVersion value");
+ return false;
+ }
+ ctx.setSdkVersion(sdkVersion);
+ } else if (ctx.generateVersionLoadCommand()) {
+ // If we don't have an sdk version, but were going to emit a load command
+ // with min_version, then we need to give a warning as we have no sdk
+ // version to put in that command.
+ // FIXME: We need to decide whether to make this an error.
+ warn("-sdk_version is required when emitting min version load command. "
+ "Setting sdk version to match provided min version");
+ ctx.setSdkVersion(ctx.osMinVersion());
+ }
+
+ // Handle source_version
+ if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_source_version)) {
+ uint64_t version = 0;
+ if (MachOLinkingContext::parsePackedVersion(arg->getValue(),
+ version)) {
+ error("malformed source_version value");
+ return false;
+ }
+ ctx.setSourceVersion(version);
+ }
+
+ // Handle stack_size
+ if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) {
+ uint64_t stackSizeVal;
+ if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) {
+ error("stack_size expects a hex number");
+ return false;
+ }
+ if ((stackSizeVal % ctx.pageSize()) != 0) {
+ error("stack_size must be a multiple of page size (0x" +
+ llvm::utohexstr(ctx.pageSize()) + ")");
+ return false;
+ }
+
+ ctx.setStackSize(stackSizeVal);
+ }
+
+ // Handle debug info handling options: -S
+ if (parsedArgs.hasArg(OPT_S))
+ ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap);
+
+ // Handle -order_file <file>
+ for (auto orderFile : parsedArgs.filtered(OPT_order_file)) {
+ if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx)) {
+ error(ec.message() + ", processing '-order_file " + orderFile->getValue()
+ + "'");
+ return false;
+ }
+ }
+
+ // Handle -flat_namespace.
+ if (llvm::opt::Arg *ns =
+ parsedArgs.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) {
+ if (ns->getOption().getID() == OPT_flat_namespace)
+ ctx.setUseFlatNamespace(true);
+ }
+
+ // Handle -undefined
+ if (llvm::opt::Arg *undef = parsedArgs.getLastArg(OPT_undefined)) {
+ MachOLinkingContext::UndefinedMode UndefMode;
+ if (StringRef(undef->getValue()).equals("error"))
+ UndefMode = MachOLinkingContext::UndefinedMode::error;
+ else if (StringRef(undef->getValue()).equals("warning"))
+ UndefMode = MachOLinkingContext::UndefinedMode::warning;
+ else if (StringRef(undef->getValue()).equals("suppress"))
+ UndefMode = MachOLinkingContext::UndefinedMode::suppress;
+ else if (StringRef(undef->getValue()).equals("dynamic_lookup"))
+ UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup;
+ else {
+ error("invalid option to -undefined [ warning | error | suppress | "
+ "dynamic_lookup ]");
+ return false;
+ }
+
+ if (ctx.useFlatNamespace()) {
+ // If we're using -flat_namespace then 'warning', 'suppress' and
+ // 'dynamic_lookup' are all equivalent, so map them to 'suppress'.
+ if (UndefMode != MachOLinkingContext::UndefinedMode::error)
+ UndefMode = MachOLinkingContext::UndefinedMode::suppress;
+ } else {
+ // If we're using -twolevel_namespace then 'warning' and 'suppress' are
+ // illegal. Emit a diagnostic if they've been (mis)used.
+ if (UndefMode == MachOLinkingContext::UndefinedMode::warning ||
+ UndefMode == MachOLinkingContext::UndefinedMode::suppress) {
+ error("can't use -undefined warning or suppress with "
+ "-twolevel_namespace");
+ return false;
+ }
+ }
+
+ ctx.setUndefinedMode(UndefMode);
+ }
+
+ // Handle -no_objc_category_merging.
+ if (parsedArgs.getLastArg(OPT_no_objc_category_merging))
+ ctx.setMergeObjCCategories(false);
+
+ // Handle -rpath <path>
+ if (parsedArgs.hasArg(OPT_rpath)) {
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_EXECUTE:
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ if (!ctx.minOS("10.5", "2.0")) {
+ if (ctx.os() == MachOLinkingContext::OS::macOSX)
+ error("-rpath can only be used when targeting OS X 10.5 or later");
+ else
+ error("-rpath can only be used when targeting iOS 2.0 or later");
+ return false;
+ }
+ break;
+ default:
+ error("-rpath can only be used when creating a dynamic final linked "
+ "image");
+ return false;
+ }
+
+ for (auto rPath : parsedArgs.filtered(OPT_rpath)) {
+ ctx.addRpath(rPath->getValue());
+ }
+ }
+
+ // Parse the LLVM options before we process files in case the file handling
+ // makes use of things like LLVM_DEBUG().
+ parseLLVMOptions(ctx);
+
+ // Handle input files and sectcreate.
+ for (auto &arg : parsedArgs) {
+ bool upward;
+ llvm::Optional<StringRef> resolvedPath;
+ switch (arg->getOption().getID()) {
+ default:
+ continue;
+ case OPT_INPUT:
+ addFile(arg->getValue(), ctx, globalWholeArchive, false);
+ break;
+ case OPT_upward_library:
+ addFile(arg->getValue(), ctx, false, true);
+ break;
+ case OPT_force_load:
+ addFile(arg->getValue(), ctx, true, false);
+ break;
+ case OPT_l:
+ case OPT_upward_l:
+ upward = (arg->getOption().getID() == OPT_upward_l);
+ resolvedPath = ctx.searchLibrary(arg->getValue());
+ if (!resolvedPath) {
+ error("Unable to find library for " + arg->getSpelling() +
+ arg->getValue());
+ return false;
+ } else if (ctx.testingFileUsage()) {
+ message(Twine("Found ") + (upward ? "upward " : " ") + "library " +
+ canonicalizePath(resolvedPath.getValue()));
+ }
+ addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward);
+ break;
+ case OPT_framework:
+ case OPT_upward_framework:
+ upward = (arg->getOption().getID() == OPT_upward_framework);
+ resolvedPath = ctx.findPathForFramework(arg->getValue());
+ if (!resolvedPath) {
+ error("Unable to find framework for " + arg->getSpelling() + " " +
+ arg->getValue());
+ return false;
+ } else if (ctx.testingFileUsage()) {
+ message(Twine("Found ") + (upward ? "upward " : " ") + "framework " +
+ canonicalizePath(resolvedPath.getValue()));
+ }
+ addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward);
+ break;
+ case OPT_filelist:
+ if (auto ec = loadFileList(arg->getValue(), ctx, globalWholeArchive)) {
+ handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) {
+ error(EI.message() + ", processing '-filelist " + arg->getValue());
+ });
+ return false;
+ }
+ break;
+ case OPT_sectcreate: {
+ const char* seg = arg->getValue(0);
+ const char* sect = arg->getValue(1);
+ const char* fileName = arg->getValue(2);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> contentOrErr =
+ MemoryBuffer::getFile(fileName);
+
+ if (!contentOrErr) {
+ error("can't open -sectcreate file " + Twine(fileName));
+ return false;
+ }
+
+ ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr));
+ }
+ break;
+ }
+ }
+
+ if (ctx.getNodes().empty()) {
+ error("No input files");
+ return false;
+ }
+
+ // Validate the combination of options used.
+ return ctx.validate();
+}
+
+static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
+ std::vector<std::unique_ptr<File>> Files;
+ if (Implicit)
+ ctx.createImplicitFiles(Files);
+ else
+ ctx.createInternalFiles(Files);
+ for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
+ auto &members = ctx.getNodes();
+ members.insert(members.begin(), std::make_unique<FileNode>(std::move(*i)));
+ }
+}
+
+/// This is where the link is actually performed.
+bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
+ raw_ostream &StdoutOS, raw_ostream &StderrOS) {
+ lld::stdoutOS = &StdoutOS;
+ lld::stderrOS = &StderrOS;
+
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorLimitExceededMsg =
+ "too many errors emitted, stopping now (use "
+ "'-error-limit 0' to see all errors)";
+ errorHandler().exitEarly = CanExitEarly;
+ StderrOS.enable_colors(StderrOS.has_colors());
+
+ MachOLinkingContext ctx;
+ if (!parse(args, ctx))
+ return false;
+ if (ctx.doNothing())
+ return true;
+ if (ctx.getNodes().empty())
+ return false;
+
+ for (std::unique_ptr<Node> &ie : ctx.getNodes())
+ if (FileNode *node = dyn_cast<FileNode>(ie.get()))
+ node->getFile()->parse();
+
+ createFiles(ctx, false /* Implicit */);
+
+ // Give target a chance to add files
+ createFiles(ctx, true /* Implicit */);
+
+ // Give target a chance to postprocess input files.
+ // Mach-O uses this chance to move all object files before library files.
+ ctx.finalizeInputFiles();
+
+ // Do core linking.
+ ScopedTask resolveTask(getDefaultDomain(), "Resolve");
+ Resolver resolver(ctx);
+ if (!resolver.resolve())
+ return false;
+ SimpleFile *merged = nullptr;
+ {
+ std::unique_ptr<SimpleFile> mergedFile = resolver.resultFile();
+ merged = mergedFile.get();
+ auto &members = ctx.getNodes();
+ members.insert(members.begin(),
+ std::make_unique<FileNode>(std::move(mergedFile)));
+ }
+ resolveTask.end();
+
+ // Run passes on linked atoms.
+ ScopedTask passTask(getDefaultDomain(), "Passes");
+ PassManager pm;
+ ctx.addPasses(pm);
+ if (auto ec = pm.runOnFile(*merged)) {
+ // FIXME: This should be passed to logAllUnhandledErrors but it needs
+ // to be passed a Twine instead of a string.
+ lld::errs() << "Failed to run passes on file '" << ctx.outputPath()
+ << "': ";
+ logAllUnhandledErrors(std::move(ec), lld::errs(), std::string());
+ return false;
+ }
+
+ passTask.end();
+
+ // Give linked atoms to Writer to generate output file.
+ ScopedTask writeTask(getDefaultDomain(), "Write");
+ if (auto ec = ctx.writeFile(*merged)) {
+ // FIXME: This should be passed to logAllUnhandledErrors but it needs
+ // to be passed a Twine instead of a string.
+ lld::errs() << "Failed to write file '" << ctx.outputPath() << "': ";
+ logAllUnhandledErrors(std::move(ec), lld::errs(), std::string());
+ return false;
+ }
+
+ // Call exit() if we can to avoid calling destructors.
+ if (CanExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+
+ return true;
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+include "llvm/Option/OptParser.td"
+
+
+// output kinds
+def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">;
+def relocatable : Flag<["-"], "r">,
+ HelpText<"Create relocatable object file">, Group<grp_kind>;
+def static : Flag<["-"], "static">,
+ HelpText<"Create static executable">, Group<grp_kind>;
+def dynamic : Flag<["-"], "dynamic">,
+ HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
+def dylib : Flag<["-"], "dylib">,
+ HelpText<"Create dynamic library">, Group<grp_kind>;
+def bundle : Flag<["-"], "bundle">,
+ HelpText<"Create dynamic bundle">, Group<grp_kind>;
+def execute : Flag<["-"], "execute">,
+ HelpText<"Create main executable (default)">, Group<grp_kind>;
+def preload : Flag<["-"], "preload">,
+ HelpText<"Create binary for use with embedded systems">, Group<grp_kind>;
+
+// optimizations
+def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
+def dead_strip : Flag<["-"], "dead_strip">,
+ HelpText<"Remove unreference code and data">, Group<grp_opts>;
+def macosx_version_min : Separate<["-"], "macosx_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum Mac OS X version">, Group<grp_opts>;
+def ios_version_min : Separate<["-"], "ios_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum iOS version">, Group<grp_opts>;
+def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
+ Alias<ios_version_min>;
+def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum iOS simulator version">, Group<grp_opts>;
+def sdk_version : Separate<["-"], "sdk_version">,
+ MetaVarName<"<version>">,
+ HelpText<"SDK version">, Group<grp_opts>;
+def source_version : Separate<["-"], "source_version">,
+ MetaVarName<"<version>">,
+ HelpText<"Source version">, Group<grp_opts>;
+def version_load_command : Flag<["-"], "version_load_command">,
+ HelpText<"Force generation of a version load command">, Group<grp_opts>;
+def no_version_load_command : Flag<["-"], "no_version_load_command">,
+ HelpText<"Disable generation of a version load command">, Group<grp_opts>;
+def function_starts : Flag<["-"], "function_starts">,
+ HelpText<"Force generation of a function starts load command">,
+ Group<grp_opts>;
+def no_function_starts : Flag<["-"], "no_function_starts">,
+ HelpText<"Disable generation of a function starts load command">,
+ Group<grp_opts>;
+def data_in_code_info : Flag<["-"], "data_in_code_info">,
+ HelpText<"Force generation of a data in code load command">,
+ Group<grp_opts>;
+def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">,
+ HelpText<"Disable generation of a data in code load command">,
+ Group<grp_opts>;
+def mllvm : Separate<["-"], "mllvm">,
+ MetaVarName<"<option>">,
+ HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>;
+def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
+ MetaVarName<"<file-path>">,
+ HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
+def exported_symbol : Separate<["-"], "exported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
+def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+ MetaVarName<"<file-path>">,
+ HelpText<"Lists symbols that should not be exported">, Group<grp_opts>;
+def unexported_symbol : Separate<["-"], "unexported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"A symbol which should not be exported">, Group<grp_opts>;
+def keep_private_externs : Flag<["-"], "keep_private_externs">,
+ HelpText<"Private extern (hidden) symbols should not be transformed "
+ "into local symbols">, Group<grp_opts>;
+def order_file : Separate<["-"], "order_file">,
+ MetaVarName<"<file-path>">,
+ HelpText<"re-order and move specified symbols to start of their section">,
+ Group<grp_opts>;
+def flat_namespace : Flag<["-"], "flat_namespace">,
+ HelpText<"Resolves symbols in any (transitively) linked dynamic libraries. "
+ "Source libraries are not recorded: dyld will re-search all "
+ "images at runtime and use the first definition found.">,
+ Group<grp_opts>;
+def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
+ HelpText<"Resolves symbols in listed libraries only. Source libraries are "
+ "recorded in the symbol table.">,
+ Group<grp_opts>;
+def undefined : Separate<["-"], "undefined">,
+ MetaVarName<"<undefined>">,
+ HelpText<"Determines how undefined symbols are handled.">,
+ Group<grp_opts>;
+def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
+ HelpText<"Disables the optimisation which merges Objective-C categories "
+ "on a class in to the class itself.">,
+ Group<grp_opts>;
+
+// main executable options
+def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">;
+def entry : Separate<["-"], "e">,
+ MetaVarName<"<entry-name>">,
+ HelpText<"entry symbol name">,Group<grp_main>;
+def pie : Flag<["-"], "pie">,
+ HelpText<"Create Position Independent Executable (for ASLR)">,
+ Group<grp_main>;
+def no_pie : Flag<["-"], "no_pie">,
+ HelpText<"Do not create Position Independent Executable">,
+ Group<grp_main>;
+def stack_size : Separate<["-"], "stack_size">,
+ HelpText<"Specifies the maximum stack size for the main thread in a program. "
+ "Must be a page-size multiple. (default=8Mb)">,
+ Group<grp_main>;
+def export_dynamic : Flag<["-"], "export_dynamic">,
+ HelpText<"Preserves all global symbols in main executables during LTO">,
+ Group<grp_main>;
+
+// dylib executable options
+def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
+def install_name : Separate<["-"], "install_name">,
+ MetaVarName<"<path>">,
+ HelpText<"The dylib's install name">, Group<grp_dylib>;
+def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
+ HelpText<"Marks the dylib as having no side effects during initialization">,
+ Group<grp_dylib>;
+def compatibility_version : Separate<["-"], "compatibility_version">,
+ MetaVarName<"<version>">,
+ HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
+def current_version : Separate<["-"], "current_version">,
+ MetaVarName<"<version>">,
+ HelpText<"The dylib's current version">, Group<grp_dylib>;
+
+// dylib executable options - compatibility aliases
+def dylib_install_name : Separate<["-"], "dylib_install_name">,
+ Alias<install_name>;
+def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
+ MetaVarName<"<version>">, Alias<compatibility_version>;
+def dylib_current_version : Separate<["-"], "dylib_current_version">,
+ MetaVarName<"<version>">, Alias<current_version>;
+
+// bundle executable options
+def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
+def bundle_loader : Separate<["-"], "bundle_loader">,
+ MetaVarName<"<path>">,
+ HelpText<"The executable that will be loading this Mach-O bundle">,
+ Group<grp_bundle>;
+
+// library options
+def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
+def L : JoinedOrSeparate<["-"], "L">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add directory to library search path">, Group<grp_libs>;
+def F : JoinedOrSeparate<["-"], "F">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add directory to framework search path">, Group<grp_libs>;
+def Z : Flag<["-"], "Z">,
+ HelpText<"Do not search standard directories for libraries or frameworks">;
+def all_load : Flag<["-"], "all_load">,
+ HelpText<"Forces all members of all static libraries to be loaded">,
+ Group<grp_libs>;
+def force_load : Separate<["-"], "force_load">,
+ MetaVarName<"<library-path>">,
+ HelpText<"Forces all members of specified static libraries to be loaded">,
+ Group<grp_libs>;
+def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">,
+ HelpText<"Add path to SDK to all absolute library search paths">,
+ Group<grp_libs>;
+
+// Input options
+def l : Joined<["-"], "l">,
+ MetaVarName<"<libname>">,
+ HelpText<"Base name of library searched for in -L directories">;
+def upward_l : Joined<["-"], "upward-l">,
+ MetaVarName<"<libname>">,
+ HelpText<"Base name of upward library searched for in -L directories">;
+def framework : Separate<["-"], "framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Base name of framework searched for in -F directories">;
+def upward_framework : Separate<["-"], "upward_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Base name of upward framework searched for in -F directories">;
+def upward_library : Separate<["-"], "upward_library">,
+ MetaVarName<"<path>">,
+ HelpText<"path to upward dylib to link with">;
+def filelist : Separate<["-"], "filelist">,
+ MetaVarName<"<path>">,
+ HelpText<"file containing paths to input files">;
+
+
+// test case options
+def print_atoms : Flag<["-"], "print_atoms">,
+ HelpText<"Emit output as yaml atoms">;
+def test_file_usage : Flag<["-"], "test_file_usage">,
+ HelpText<"Only files specified by -file_exists are considered to exist. "
+ "Print which files would be used">;
+def path_exists : Separate<["-"], "path_exists">,
+ MetaVarName<"<path>">,
+ HelpText<"Used with -test_file_usage to declare a path">;
+
+
+// general options
+def output : Separate<["-"], "o">,
+ MetaVarName<"<path>">,
+ HelpText<"Output file path">;
+def arch : Separate<["-"], "arch">,
+ MetaVarName<"<arch-name>">,
+ HelpText<"Architecture to link">;
+def sectalign : MultiArg<["-"], "sectalign", 3>,
+ MetaVarName<"<segname> <sectname> <alignment>">,
+ HelpText<"Alignment for segment/section">;
+def sectcreate : MultiArg<["-"], "sectcreate", 3>,
+ MetaVarName<"<segname> <sectname> <file>">,
+ HelpText<"Create section <segname>/<sectname> from contents of <file>">;
+def image_base : Separate<["-"], "image_base">;
+def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>;
+def demangle : Flag<["-"], "demangle">,
+ HelpText<"Demangles symbol names in errors and warnings">;
+def dependency_info : Separate<["-"], "dependency_info">,
+ MetaVarName<"<file>">,
+ HelpText<"Write binary list of files used during link">;
+def S : Flag<["-"], "S">,
+ HelpText<"Remove debug information (STABS or DWARF) from the output file">;
+def rpath : Separate<["-"], "rpath">,
+ MetaVarName<"<path>">,
+ HelpText<"Add path to the runpath search path list for image being created">;
+
+def t : Flag<["-"], "t">,
+ HelpText<"Print the names of the input files as ld processes them">;
+def v : Flag<["-"], "v">,
+ HelpText<"Print linker information">;
+def error_limit : Separate<["-", "--"], "error-limit">,
+ MetaVarName<"<number>">,
+ HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+
+// Ignored options
+def lto_library : Separate<["-"], "lto_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Ignored for compatibility with other linkers">;
+
+// Obsolete options
+def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">;
+def single_module : Flag<["-"], "single_module">,
+ HelpText<"Default for dylibs">, Group<grp_obsolete>;
+def multi_module : Flag<["-"], "multi_module">,
+ HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>;
+def objc_gc_compaction : Flag<["-"], "objc_gc_compaction">,
+ HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
+def objc_gc : Flag<["-"], "objc_gc">,
+ HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
+def objc_gc_only : Flag<["-"], "objc_gc_only">,
+ HelpText<"Unsupported ObjC GC option">, Group<grp_obsolete>;
--- /dev/null
+add_subdirectory(MachO)
+add_subdirectory(YAML)
+
+if (MSVC)
+ add_definitions(-wd4062) # Suppress 'warning C4062: Enumerator has no associated handler in a switch statement.'
+endif()
+
+add_lld_library(lldReaderWriter
+ FileArchive.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${LLD_INCLUDE_DIR}/lld/ReaderWriter
+
+ LINK_COMPONENTS
+ Object
+ Support
+
+ LINK_LIBS
+ lldCore
+ )
--- /dev/null
+//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/LLVM.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reader.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <set>
+#include <string>
+#include <system_error>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+using llvm::object::Archive;
+using llvm::file_magic;
+using llvm::identify_magic;
+
+namespace lld {
+
+namespace {
+
+/// The FileArchive class represents an Archive Library file
+class FileArchive : public lld::ArchiveLibraryFile {
+public:
+ FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®,
+ StringRef path, bool logLoading)
+ : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
+ _registry(reg), _logLoading(logLoading) {}
+
+ /// Check if any member of the archive contains an Atom with the
+ /// specified name and return the File object for that member, or nullptr.
+ File *find(StringRef name) override {
+ auto member = _symbolMemberMap.find(name);
+ if (member == _symbolMemberMap.end())
+ return nullptr;
+ Archive::Child c = member->second;
+
+ // Don't return a member already returned
+ Expected<StringRef> buf = c.getBuffer();
+ if (!buf) {
+ // TODO: Actually report errors helpfully.
+ consumeError(buf.takeError());
+ return nullptr;
+ }
+ const char *memberStart = buf->data();
+ if (_membersInstantiated.count(memberStart))
+ return nullptr;
+ _membersInstantiated.insert(memberStart);
+
+ std::unique_ptr<File> result;
+ if (instantiateMember(c, result))
+ return nullptr;
+
+ File *file = result.get();
+ _filesReturned.push_back(std::move(result));
+
+ // Give up the file pointer. It was stored and will be destroyed with destruction of FileArchive
+ return file;
+ }
+
+ /// parse each member
+ std::error_code
+ parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
+ if (std::error_code ec = parse())
+ return ec;
+ llvm::Error err = llvm::Error::success();
+ for (auto mf = _archive->child_begin(err), me = _archive->child_end();
+ mf != me; ++mf) {
+ std::unique_ptr<File> file;
+ if (std::error_code ec = instantiateMember(*mf, file)) {
+ // err is Success (or we wouldn't be in the loop body) but we can't
+ // return without testing or consuming it.
+ consumeError(std::move(err));
+ return ec;
+ }
+ result.push_back(std::move(file));
+ }
+ if (err)
+ return errorToErrorCode(std::move(err));
+ return std::error_code();
+ }
+
+ const AtomRange<DefinedAtom> defined() const override {
+ return _noDefinedAtoms;
+ }
+
+ const AtomRange<UndefinedAtom> undefined() const override {
+ return _noUndefinedAtoms;
+ }
+
+ const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
+ return _noSharedLibraryAtoms;
+ }
+
+ const AtomRange<AbsoluteAtom> absolute() const override {
+ return _noAbsoluteAtoms;
+ }
+
+ void clearAtoms() override {
+ _noDefinedAtoms.clear();
+ _noUndefinedAtoms.clear();
+ _noSharedLibraryAtoms.clear();
+ _noAbsoluteAtoms.clear();
+ }
+
+protected:
+ std::error_code doParse() override {
+ // Make Archive object which will be owned by FileArchive object.
+ llvm::Error Err = llvm::Error::success();
+ _archive.reset(new Archive(_mb->getMemBufferRef(), Err));
+ if (Err)
+ return errorToErrorCode(std::move(Err));
+ std::error_code ec;
+ if ((ec = buildTableOfContents()))
+ return ec;
+ return std::error_code();
+ }
+
+private:
+ std::error_code instantiateMember(Archive::Child member,
+ std::unique_ptr<File> &result) const {
+ Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
+ if (!mbOrErr)
+ return errorToErrorCode(mbOrErr.takeError());
+ llvm::MemoryBufferRef mb = mbOrErr.get();
+ std::string memberPath = (_archive->getFileName() + "("
+ + mb.getBufferIdentifier() + ")").str();
+
+ if (_logLoading)
+ llvm::errs() << memberPath << "\n";
+
+ std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
+ mb.getBuffer(), mb.getBufferIdentifier(), false));
+
+ ErrorOr<std::unique_ptr<File>> fileOrErr =
+ _registry.loadFile(std::move(memberMB));
+ if (std::error_code ec = fileOrErr.getError())
+ return ec;
+ result = std::move(fileOrErr.get());
+ if (std::error_code ec = result->parse())
+ return ec;
+ result->setArchivePath(_archive->getFileName());
+
+ // The memory buffer is co-owned by the archive file and the children,
+ // so that the bufffer is deallocated when all the members are destructed.
+ result->setSharedMemoryBuffer(_mb);
+ return std::error_code();
+ }
+
+ std::error_code buildTableOfContents() {
+ DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
+ << "Table of contents for archive '"
+ << _archive->getFileName() << "':\n");
+ for (const Archive::Symbol &sym : _archive->symbols()) {
+ StringRef name = sym.getName();
+ Expected<Archive::Child> memberOrErr = sym.getMember();
+ if (!memberOrErr)
+ return errorToErrorCode(memberOrErr.takeError());
+ Archive::Child member = memberOrErr.get();
+ DEBUG_WITH_TYPE("FileArchive",
+ llvm::dbgs()
+ << llvm::format("0x%08llX ",
+ member.getBuffer()->data())
+ << "'" << name << "'\n");
+ _symbolMemberMap.insert(std::make_pair(name, member));
+ }
+ return std::error_code();
+ }
+
+ typedef std::unordered_map<StringRef, Archive::Child> MemberMap;
+ typedef std::set<const char *> InstantiatedSet;
+
+ std::shared_ptr<MemoryBuffer> _mb;
+ const Registry &_registry;
+ std::unique_ptr<Archive> _archive;
+ MemberMap _symbolMemberMap;
+ InstantiatedSet _membersInstantiated;
+ bool _logLoading;
+ std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
+ std::vector<std::unique_ptr<File>> _filesReturned;
+};
+
+class ArchiveReader : public Reader {
+public:
+ ArchiveReader(bool logLoading) : _logLoading(logLoading) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef) const override {
+ return magic == file_magic::archive;
+ }
+
+ ErrorOr<std::unique_ptr<File>> loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®) const override {
+ StringRef path = mb->getBufferIdentifier();
+ std::unique_ptr<File> ret =
+ std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
+ return std::move(ret);
+ }
+
+private:
+ bool _logLoading;
+};
+
+} // anonymous namespace
+
+void Registry::addSupportArchives(bool logLoading) {
+ add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
+}
+
+} // namespace lld
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+namespace lld {
+namespace mach_o {
+
+
+ArchHandler::ArchHandler() {
+}
+
+ArchHandler::~ArchHandler() {
+}
+
+std::unique_ptr<mach_o::ArchHandler> ArchHandler::create(
+ MachOLinkingContext::Arch arch) {
+ switch (arch) {
+ case MachOLinkingContext::arch_x86_64:
+ return create_x86_64();
+ case MachOLinkingContext::arch_x86:
+ return create_x86();
+ case MachOLinkingContext::arch_armv6:
+ case MachOLinkingContext::arch_armv7:
+ case MachOLinkingContext::arch_armv7s:
+ return create_arm();
+ case MachOLinkingContext::arch_arm64:
+ return create_arm64();
+ default:
+ llvm_unreachable("Unknown arch");
+ }
+}
+
+
+bool ArchHandler::isLazyPointer(const Reference &ref) {
+ // A lazy bind entry is needed for a lazy pointer.
+ const StubInfo &info = stubInfo();
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ if (ref.kindArch() != info.lazyPointerReferenceToFinal.arch)
+ return false;
+ return (ref.kindValue() == info.lazyPointerReferenceToFinal.kind);
+}
+
+
+ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) {
+ assert((reloc.type & 0xFFF0) == 0);
+ uint16_t result = reloc.type;
+ if (reloc.scattered)
+ result |= rScattered;
+ if (reloc.pcRel)
+ result |= rPcRel;
+ if (reloc.isExtern)
+ result |= rExtern;
+ switch(reloc.length) {
+ case 0:
+ break;
+ case 1:
+ result |= rLength2;
+ break;
+ case 2:
+ result |= rLength4;
+ break;
+ case 3:
+ result |= rLength8;
+ break;
+ default:
+ llvm_unreachable("bad r_length");
+ }
+ return result;
+}
+
+normalized::Relocation
+ArchHandler::relocFromPattern(ArchHandler::RelocPattern pattern) {
+ normalized::Relocation result;
+ result.offset = 0;
+ result.scattered = (pattern & rScattered);
+ result.type = (RelocationInfoType)(pattern & 0xF);
+ result.pcRel = (pattern & rPcRel);
+ result.isExtern = (pattern & rExtern);
+ result.value = 0;
+ result.symbol = 0;
+ switch (pattern & 0x300) {
+ case rLength1:
+ result.length = 0;
+ break;
+ case rLength2:
+ result.length = 1;
+ break;
+ case rLength4:
+ result.length = 2;
+ break;
+ case rLength8:
+ result.length = 3;
+ break;
+ }
+ return result;
+}
+
+void ArchHandler::appendReloc(normalized::Relocations &relocs, uint32_t offset,
+ uint32_t symbol, uint32_t value,
+ RelocPattern pattern) {
+ normalized::Relocation reloc = relocFromPattern(pattern);
+ reloc.offset = offset;
+ reloc.symbol = symbol;
+ reloc.value = value;
+ relocs.push_back(reloc);
+}
+
+
+int16_t ArchHandler::readS16(const uint8_t *addr, bool isBig) {
+ return read16(addr, isBig);
+}
+
+int32_t ArchHandler::readS32(const uint8_t *addr, bool isBig) {
+ return read32(addr, isBig);
+}
+
+uint32_t ArchHandler::readU32(const uint8_t *addr, bool isBig) {
+ return read32(addr, isBig);
+}
+
+ int64_t ArchHandler::readS64(const uint8_t *addr, bool isBig) {
+ return read64(addr, isBig);
+}
+
+bool ArchHandler::isDwarfCIE(bool isBig, const DefinedAtom *atom) {
+ assert(atom->contentType() == DefinedAtom::typeCFI);
+ if (atom->rawContent().size() < sizeof(uint32_t))
+ return false;
+ uint32_t size = read32(atom->rawContent().data(), isBig);
+
+ uint32_t idOffset = sizeof(uint32_t);
+ if (size == 0xffffffffU)
+ idOffset += sizeof(uint64_t);
+
+ return read32(atom->rawContent().data() + idOffset, isBig) == 0;
+}
+
+const Atom *ArchHandler::fdeTargetFunction(const DefinedAtom *fde) {
+ for (auto ref : *fde) {
+ if (ref->kindNamespace() == Reference::KindNamespace::mach_o &&
+ ref->kindValue() == unwindRefToFunctionKind()) {
+ assert(ref->kindArch() == kindArch() && "unexpected Reference arch");
+ return ref->target();
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace mach_o
+} // namespace lld
+
+
+
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
+#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
+
+#include "Atoms.h"
+#include "File.h"
+#include "MachONormalizedFile.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/Triple.h"
+
+namespace lld {
+namespace mach_o {
+
+///
+/// The ArchHandler class handles all architecture specific aspects of
+/// mach-o linking.
+///
+class ArchHandler {
+public:
+ virtual ~ArchHandler();
+
+ /// There is no public interface to subclasses of ArchHandler, so this
+ /// is the only way to instantiate an ArchHandler.
+ static std::unique_ptr<ArchHandler> create(MachOLinkingContext::Arch arch);
+
+ /// Get (arch specific) kind strings used by Registry.
+ virtual const Registry::KindStrings *kindStrings() = 0;
+
+ /// Convert mach-o Arch to Reference::KindArch.
+ virtual Reference::KindArch kindArch() = 0;
+
+ /// Used by StubPass to update References to shared library functions
+ /// to be references to a stub.
+ virtual bool isCallSite(const Reference &) = 0;
+
+ /// Used by GOTPass to locate GOT References
+ virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) {
+ return false;
+ }
+
+ /// Used by TLVPass to locate TLV References.
+ virtual bool isTLVAccess(const Reference &) const { return false; }
+
+ /// Used by the TLVPass to update TLV References.
+ virtual void updateReferenceToTLV(const Reference *) {}
+
+ /// Used by ShimPass to insert shims in branches that switch mode.
+ virtual bool isNonCallBranch(const Reference &) = 0;
+
+ /// Used by GOTPass to update GOT References
+ virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {}
+
+ /// Does this architecture make use of __unwind_info sections for exception
+ /// handling? If so, it will need a separate pass to create them.
+ virtual bool needsCompactUnwind() = 0;
+
+ /// Returns the kind of reference to use to synthesize a 32-bit image-offset
+ /// value, used in the __unwind_info section.
+ virtual Reference::KindValue imageOffsetKind() = 0;
+
+ /// Returns the kind of reference to use to synthesize a 32-bit image-offset
+ /// indirect value. Used for personality functions in the __unwind_info
+ /// section.
+ virtual Reference::KindValue imageOffsetKindIndirect() = 0;
+
+ /// Architecture specific compact unwind type that signals __eh_frame should
+ /// actually be used.
+ virtual uint32_t dwarfCompactUnwindType() = 0;
+
+ /// Reference from an __eh_frame CIE atom to its personality function it's
+ /// describing. Usually pointer-sized and PC-relative, but differs in whether
+ /// it needs to be in relocatable objects.
+ virtual Reference::KindValue unwindRefToPersonalityFunctionKind() = 0;
+
+ /// Reference from an __eh_frame FDE to the CIE it's based on.
+ virtual Reference::KindValue unwindRefToCIEKind() = 0;
+
+ /// Reference from an __eh_frame FDE atom to the function it's
+ /// describing. Usually pointer-sized and PC-relative, but differs in whether
+ /// it needs to be in relocatable objects.
+ virtual Reference::KindValue unwindRefToFunctionKind() = 0;
+
+ /// Reference from an __unwind_info entry of dwarfCompactUnwindType to the
+ /// required __eh_frame entry. On current architectures, the low 24 bits
+ /// represent the offset of the function's FDE entry from the start of
+ /// __eh_frame.
+ virtual Reference::KindValue unwindRefToEhFrameKind() = 0;
+
+ /// Returns a pointer sized reference kind. On 64-bit targets this will
+ /// likely be something like pointer64, and pointer32 on 32-bit targets.
+ virtual Reference::KindValue pointerKind() = 0;
+
+ virtual const Atom *fdeTargetFunction(const DefinedAtom *fde);
+
+ /// Used by normalizedFromAtoms() to know where to generated rebasing and
+ /// binding info in final executables.
+ virtual bool isPointer(const Reference &) = 0;
+
+ /// Used by normalizedFromAtoms() to know where to generated lazy binding
+ /// info in final executables.
+ virtual bool isLazyPointer(const Reference &);
+
+ /// Reference from an __stub_helper entry to the required offset of the
+ /// lazy bind commands.
+ virtual Reference::KindValue lazyImmediateLocationKind() = 0;
+
+ /// Returns true if the specified relocation is paired to the next relocation.
+ virtual bool isPairedReloc(const normalized::Relocation &) = 0;
+
+ /// Prototype for a helper function. Given a sectionIndex and address,
+ /// finds the atom and offset with that atom of that address.
+ typedef std::function<llvm::Error (uint32_t sectionIndex, uint64_t addr,
+ const lld::Atom **, Reference::Addend *)>
+ FindAtomBySectionAndAddress;
+
+ /// Prototype for a helper function. Given a symbolIndex, finds the atom
+ /// representing that symbol.
+ typedef std::function<llvm::Error (uint32_t symbolIndex,
+ const lld::Atom **)> FindAtomBySymbolIndex;
+
+ /// Analyzes a relocation from a .o file and returns the info
+ /// (kind, target, addend) needed to instantiate a Reference.
+ /// Two helper functions are passed as parameters to find the target atom
+ /// given a symbol index or address.
+ virtual llvm::Error
+ getReferenceInfo(const normalized::Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBigEndian,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) = 0;
+
+ /// Analyzes a pair of relocations from a .o file and returns the info
+ /// (kind, target, addend) needed to instantiate a Reference.
+ /// Two helper functions are passed as parameters to find the target atom
+ /// given a symbol index or address.
+ virtual llvm::Error
+ getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig, bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) = 0;
+
+ /// Prototype for a helper function. Given an atom, finds the symbol table
+ /// index for it in the output file.
+ typedef std::function<uint32_t (const Atom &atom)> FindSymbolIndexForAtom;
+
+ /// Prototype for a helper function. Given an atom, finds the index
+ /// of the section that will contain the atom.
+ typedef std::function<uint32_t (const Atom &atom)> FindSectionIndexForAtom;
+
+ /// Prototype for a helper function. Given an atom, finds the address
+ /// assigned to it in the output file.
+ typedef std::function<uint64_t (const Atom &atom)> FindAddressForAtom;
+
+ /// Some architectures require local symbols on anonymous atoms.
+ virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) {
+ return false;
+ }
+
+ /// Copy raw content then apply all fixup References on an Atom.
+ virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) = 0;
+
+ /// Used in -r mode to convert a Reference to a mach-o relocation.
+ virtual void appendSectionRelocations(const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom,
+ FindSectionIndexForAtom,
+ FindAddressForAtom,
+ normalized::Relocations&) = 0;
+
+ /// Add arch-specific References.
+ virtual void addAdditionalReferences(MachODefinedAtom &atom) { }
+
+ // Add Reference for data-in-code marker.
+ virtual void addDataInCodeReference(MachODefinedAtom &atom, uint32_t atomOff,
+ uint16_t length, uint16_t kind) { }
+
+ /// Returns true if the specificed Reference value marks the start or end
+ /// of a data-in-code range in an atom.
+ virtual bool isDataInCodeTransition(Reference::KindValue refKind) {
+ return false;
+ }
+
+ /// Returns the Reference value for a Reference that marks that start of
+ /// a data-in-code range.
+ virtual Reference::KindValue dataInCodeTransitionStart(
+ const MachODefinedAtom &atom) {
+ return 0;
+ }
+
+ /// Returns the Reference value for a Reference that marks that end of
+ /// a data-in-code range.
+ virtual Reference::KindValue dataInCodeTransitionEnd(
+ const MachODefinedAtom &atom) {
+ return 0;
+ }
+
+ /// Only relevant for 32-bit arm archs.
+ virtual bool isThumbFunction(const DefinedAtom &atom) { return false; }
+
+ /// Only relevant for 32-bit arm archs.
+ virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
+ const DefinedAtom &) {
+ llvm_unreachable("shims only support on arm");
+ }
+
+ /// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE).
+ static bool isDwarfCIE(bool isBig, const DefinedAtom *atom);
+
+ struct ReferenceInfo {
+ Reference::KindArch arch;
+ uint16_t kind;
+ uint32_t offset;
+ int32_t addend;
+ };
+
+ struct OptionalRefInfo {
+ bool used;
+ uint16_t kind;
+ uint32_t offset;
+ int32_t addend;
+ };
+
+ /// Table of architecture specific information for creating stubs.
+ struct StubInfo {
+ const char* binderSymbolName;
+ ReferenceInfo lazyPointerReferenceToHelper;
+ ReferenceInfo lazyPointerReferenceToFinal;
+ ReferenceInfo nonLazyPointerReferenceToBinder;
+ uint8_t codeAlignment;
+
+ uint32_t stubSize;
+ uint8_t stubBytes[16];
+ ReferenceInfo stubReferenceToLP;
+ OptionalRefInfo optStubReferenceToLP;
+
+ uint32_t stubHelperSize;
+ uint8_t stubHelperBytes[16];
+ ReferenceInfo stubHelperReferenceToImm;
+ ReferenceInfo stubHelperReferenceToHelperCommon;
+
+ DefinedAtom::ContentType stubHelperImageCacheContentType;
+
+ uint32_t stubHelperCommonSize;
+ uint8_t stubHelperCommonAlignment;
+ uint8_t stubHelperCommonBytes[36];
+ ReferenceInfo stubHelperCommonReferenceToCache;
+ OptionalRefInfo optStubHelperCommonReferenceToCache;
+ ReferenceInfo stubHelperCommonReferenceToBinder;
+ OptionalRefInfo optStubHelperCommonReferenceToBinder;
+ };
+
+ virtual const StubInfo &stubInfo() = 0;
+
+protected:
+ ArchHandler();
+
+ static std::unique_ptr<mach_o::ArchHandler> create_x86_64();
+ static std::unique_ptr<mach_o::ArchHandler> create_x86();
+ static std::unique_ptr<mach_o::ArchHandler> create_arm();
+ static std::unique_ptr<mach_o::ArchHandler> create_arm64();
+
+ // Handy way to pack mach-o r_type and other bit fields into one 16-bit value.
+ typedef uint16_t RelocPattern;
+ enum {
+ rScattered = 0x8000,
+ rPcRel = 0x4000,
+ rExtern = 0x2000,
+ rLength1 = 0x0000,
+ rLength2 = 0x0100,
+ rLength4 = 0x0200,
+ rLength8 = 0x0300,
+ rLenArmLo = rLength1,
+ rLenArmHi = rLength2,
+ rLenThmbLo = rLength4,
+ rLenThmbHi = rLength8
+ };
+ /// Extract RelocPattern from normalized mach-o relocation.
+ static RelocPattern relocPattern(const normalized::Relocation &reloc);
+ /// Create normalized Relocation initialized from pattern.
+ static normalized::Relocation relocFromPattern(RelocPattern pattern);
+ /// One liner to add a relocation.
+ static void appendReloc(normalized::Relocations &relocs, uint32_t offset,
+ uint32_t symbol, uint32_t value,
+ RelocPattern pattern);
+
+
+ static int16_t readS16(const uint8_t *addr, bool isBig);
+ static int32_t readS32(const uint8_t *addr, bool isBig);
+ static uint32_t readU32(const uint8_t *addr, bool isBig);
+ static int64_t readS64(const uint8_t *addr, bool isBig);
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+namespace lld {
+namespace mach_o {
+
+using llvm::support::ulittle32_t;
+using llvm::support::little32_t;
+
+
+class ArchHandler_arm : public ArchHandler {
+public:
+ ArchHandler_arm() = default;
+ ~ArchHandler_arm() override = default;
+
+ const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
+
+ Reference::KindArch kindArch() override { return Reference::KindArch::ARM; }
+
+ const ArchHandler::StubInfo &stubInfo() override;
+ bool isCallSite(const Reference &) override;
+ bool isPointer(const Reference &) override;
+ bool isPairedReloc(const normalized::Relocation &) override;
+ bool isNonCallBranch(const Reference &) override;
+
+ bool needsCompactUnwind() override {
+ return false;
+ }
+ Reference::KindValue imageOffsetKind() override {
+ return invalid;
+ }
+ Reference::KindValue imageOffsetKindIndirect() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToPersonalityFunctionKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToCIEKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToFunctionKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToEhFrameKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
+ Reference::KindValue pointerKind() override {
+ return invalid;
+ }
+
+ uint32_t dwarfCompactUnwindType() override {
+ // FIXME
+ return -1;
+ }
+
+ llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+ llvm::Error
+ getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap, bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+
+ void generateAtomContent(const DefinedAtom &atom, bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
+
+ void appendSectionRelocations(const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom,
+ FindSectionIndexForAtom,
+ FindAddressForAtom,
+ normalized::Relocations &) override;
+
+ void addAdditionalReferences(MachODefinedAtom &atom) override;
+
+ bool isDataInCodeTransition(Reference::KindValue refKind) override {
+ switch (refKind) {
+ case modeThumbCode:
+ case modeArmCode:
+ case modeData:
+ return true;
+ default:
+ return false;
+ break;
+ }
+ }
+
+ Reference::KindValue dataInCodeTransitionStart(
+ const MachODefinedAtom &atom) override {
+ return modeData;
+ }
+
+ Reference::KindValue dataInCodeTransitionEnd(
+ const MachODefinedAtom &atom) override {
+ return atom.isThumb() ? modeThumbCode : modeArmCode;
+ }
+
+ bool isThumbFunction(const DefinedAtom &atom) override;
+ const DefinedAtom *createShim(MachOFile &file, bool thumbToArm,
+ const DefinedAtom &) override;
+
+private:
+ friend class Thumb2ToArmShimAtom;
+ friend class ArmToThumbShimAtom;
+
+ static const Registry::KindStrings _sKindStrings[];
+ static const StubInfo _sStubInfoArmPIC;
+
+ enum ArmKind : Reference::KindValue {
+ invalid, /// for error condition
+
+ modeThumbCode, /// Content starting at this offset is thumb.
+ modeArmCode, /// Content starting at this offset is arm.
+ modeData, /// Content starting at this offset is data.
+
+ // Kinds found in mach-o .o files:
+ thumb_bl22, /// ex: bl _foo
+ thumb_b22, /// ex: b _foo
+ thumb_movw, /// ex: movw r1, :lower16:_foo
+ thumb_movt, /// ex: movt r1, :lower16:_foo
+ thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
+ thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
+ arm_bl24, /// ex: bl _foo
+ arm_b24, /// ex: b _foo
+ arm_movw, /// ex: movw r1, :lower16:_foo
+ arm_movt, /// ex: movt r1, :lower16:_foo
+ arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4))
+ arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4))
+ pointer32, /// ex: .long _foo
+ delta32, /// ex: .long _foo - .
+
+ // Kinds introduced by Passes:
+ lazyPointer, /// Location contains a lazy pointer.
+ lazyImmediateLocation, /// Location contains immediate value used in stub.
+ };
+
+ // Utility functions for inspecting/updating instructions.
+ static bool isThumbMovw(uint32_t instruction);
+ static bool isThumbMovt(uint32_t instruction);
+ static bool isArmMovw(uint32_t instruction);
+ static bool isArmMovt(uint32_t instruction);
+ static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t);
+ static int32_t getDisplacementFromArmBranch(uint32_t instruction);
+ static uint16_t getWordFromThumbMov(uint32_t instruction);
+ static uint16_t getWordFromArmMov(uint32_t instruction);
+ static uint32_t clearThumbBit(uint32_t value, const Atom *target);
+ static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp,
+ bool targetIsThumb);
+ static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia,
+ int32_t disp, bool targetThumb);
+ static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word);
+ static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word);
+
+ StringRef stubName(const DefinedAtom &);
+ bool useExternalRelocationTo(const Atom &target);
+
+ void applyFixupFinal(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress, uint64_t targetAddress,
+ uint64_t inAtomAddress, bool &thumbMode,
+ bool targetIsThumb);
+
+ void applyFixupRelocatable(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress, bool &thumbMode,
+ bool targetIsThumb);
+};
+
+//===----------------------------------------------------------------------===//
+// ArchHandler_arm
+//===----------------------------------------------------------------------===//
+
+const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = {
+ LLD_KIND_STRING_ENTRY(invalid),
+ LLD_KIND_STRING_ENTRY(modeThumbCode),
+ LLD_KIND_STRING_ENTRY(modeArmCode),
+ LLD_KIND_STRING_ENTRY(modeData),
+ LLD_KIND_STRING_ENTRY(thumb_bl22),
+ LLD_KIND_STRING_ENTRY(thumb_b22),
+ LLD_KIND_STRING_ENTRY(thumb_movw),
+ LLD_KIND_STRING_ENTRY(thumb_movt),
+ LLD_KIND_STRING_ENTRY(thumb_movw_funcRel),
+ LLD_KIND_STRING_ENTRY(thumb_movt_funcRel),
+ LLD_KIND_STRING_ENTRY(arm_bl24),
+ LLD_KIND_STRING_ENTRY(arm_b24),
+ LLD_KIND_STRING_ENTRY(arm_movw),
+ LLD_KIND_STRING_ENTRY(arm_movt),
+ LLD_KIND_STRING_ENTRY(arm_movw_funcRel),
+ LLD_KIND_STRING_ENTRY(arm_movt_funcRel),
+ LLD_KIND_STRING_ENTRY(pointer32),
+ LLD_KIND_STRING_ENTRY(delta32),
+ LLD_KIND_STRING_ENTRY(lazyPointer),
+ LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
+ LLD_KIND_STRING_END
+};
+
+const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = {
+ "dyld_stub_binder",
+
+ // References in lazy pointer
+ { Reference::KindArch::ARM, pointer32, 0, 0 },
+ { Reference::KindArch::ARM, lazyPointer, 0, 0 },
+
+ // GOT pointer to dyld_stub_binder
+ { Reference::KindArch::ARM, pointer32, 0, 0 },
+
+ // arm code alignment 2^2
+ 2,
+
+ // Stub size and code
+ 16,
+ { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12
+ 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
+ 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8)
+ { Reference::KindArch::ARM, delta32, 12, 0 },
+ { false, 0, 0, 0 },
+
+ // Stub Helper size and code
+ 12,
+ { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0]
+ 0x00, 0x00, 0x00, 0xEA, // b _helperhelper
+ 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset
+ { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 },
+ { Reference::KindArch::ARM, arm_b24, 4, 0 },
+
+ // Stub helper image cache content type
+ DefinedAtom::typeGOT,
+
+ // Stub Helper-Common size and code
+ 36,
+ // Stub helper alignment
+ 2,
+ { // push lazy-info-offset
+ 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
+ // push address of dyld_mageLoaderCache
+ 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1
+ 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
+ 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]!
+ // jump through dyld_stub_binder
+ 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2
+ 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
+ 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16)
+ 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28)
+ { Reference::KindArch::ARM, delta32, 28, 0xC },
+ { false, 0, 0, 0 },
+ { Reference::KindArch::ARM, delta32, 32, 0x04 },
+ { false, 0, 0, 0 }
+};
+
+const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() {
+ // If multiple kinds of stubs are supported, select which StubInfo here.
+ return _sStubInfoArmPIC;
+}
+
+bool ArchHandler_arm::isCallSite(const Reference &ref) {
+ switch (ref.kindValue()) {
+ case thumb_b22:
+ case thumb_bl22:
+ case arm_b24:
+ case arm_bl24:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool ArchHandler_arm::isPointer(const Reference &ref) {
+ return (ref.kindValue() == pointer32);
+}
+
+bool ArchHandler_arm::isNonCallBranch(const Reference &ref) {
+ switch (ref.kindValue()) {
+ case thumb_b22:
+ case arm_b24:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
+ switch (reloc.type) {
+ case ARM_RELOC_SECTDIFF:
+ case ARM_RELOC_LOCAL_SECTDIFF:
+ case ARM_RELOC_HALF_SECTDIFF:
+ case ARM_RELOC_HALF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/// Trace references from stub atom to lazy pointer to target and get its name.
+StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) {
+ assert(stubAtom.contentType() == DefinedAtom::typeStub);
+ for (const Reference *ref : stubAtom) {
+ if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) {
+ if (lp->contentType() != DefinedAtom::typeLazyPointer)
+ continue;
+ for (const Reference *ref2 : *lp) {
+ if (ref2->kindValue() != lazyPointer)
+ continue;
+ return ref2->target()->name();
+ }
+ }
+ }
+ return "stub";
+}
+
+/// Extract displacement from an ARM b/bl/blx instruction.
+int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
+ // Sign-extend imm24
+ int32_t displacement = (instruction & 0x00FFFFFF) << 2;
+ if ((displacement & 0x02000000) != 0)
+ displacement |= 0xFC000000;
+ // If this is BLX and H bit set, add 2.
+ if ((instruction & 0xFF000000) == 0xFB000000)
+ displacement += 2;
+ return displacement;
+}
+
+/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed.
+uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction,
+ int32_t displacement,
+ bool targetIsThumb) {
+ assert((displacement <= 33554428) && (displacement > (-33554432))
+ && "arm branch out of range");
+ bool is_blx = ((instruction & 0xF0000000) == 0xF0000000);
+ uint32_t newInstruction = (instruction & 0xFF000000);
+ uint32_t h = 0;
+ if (targetIsThumb) {
+ // Force use of BLX.
+ newInstruction = 0xFA000000;
+ if (!is_blx) {
+ assert(((instruction & 0xF0000000) == 0xE0000000)
+ && "no conditional arm blx");
+ assert(((instruction & 0xFF000000) == 0xEB000000)
+ && "no arm pc-rel BX instruction");
+ }
+ if (displacement & 2)
+ h = 1;
+ }
+ else {
+ // Force use of B/BL.
+ if (is_blx)
+ newInstruction = 0xEB000000;
+ }
+ newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF);
+ return newInstruction;
+}
+
+/// Extract displacement from a thumb b/bl/blx instruction.
+int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction,
+ uint32_t instrAddr) {
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ uint32_t s = (instruction >> 10) & 0x1;
+ uint32_t j1 = (instruction >> 29) & 0x1;
+ uint32_t j2 = (instruction >> 27) & 0x1;
+ uint32_t imm10 = instruction & 0x3FF;
+ uint32_t imm11 = (instruction >> 16) & 0x7FF;
+ uint32_t i1 = (j1 == s);
+ uint32_t i2 = (j2 == s);
+ uint32_t dis =
+ (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
+ int32_t sdis = dis;
+ int32_t result = s ? (sdis | 0xFE000000) : sdis;
+ if (is_blx && (instrAddr & 0x2)) {
+ // The thumb blx instruction always has low bit of imm11 as zero. The way
+ // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that
+ // the blx instruction always 4-byte aligns the pc before adding the
+ // displacement from the blx. We must emulate that when decoding this.
+ result -= 2;
+ }
+ return result;
+}
+
+/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed.
+uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction,
+ uint32_t instrAddr,
+ int32_t displacement,
+ bool targetIsThumb) {
+ assert((displacement <= 16777214) && (displacement > (-16777216))
+ && "thumb branch out of range");
+ bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
+ uint32_t newInstruction = (instruction & 0xD000F800);
+ if (is_bl || is_blx) {
+ if (targetIsThumb) {
+ newInstruction = 0xD000F000; // Use bl
+ } else {
+ newInstruction = 0xC000F000; // Use blx
+ // See note in getDisplacementFromThumbBranch() about blx.
+ if (instrAddr & 0x2)
+ displacement += 2;
+ }
+ } else if (is_b) {
+ assert(targetIsThumb && "no pc-rel thumb branch instruction that "
+ "switches to arm mode");
+ }
+ else {
+ llvm_unreachable("thumb branch22 reloc on a non-branch instruction");
+ }
+ uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
+ uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
+ uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
+ uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF;
+ uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF;
+ uint32_t j1 = (i1 == s);
+ uint32_t j2 = (i2 == s);
+ uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11;
+ uint32_t firstDisp = (s << 10) | imm10;
+ newInstruction |= (nextDisp << 16) | firstDisp;
+ return newInstruction;
+}
+
+bool ArchHandler_arm::isThumbMovw(uint32_t instruction) {
+ return (instruction & 0x8000FBF0) == 0x0000F240;
+}
+
+bool ArchHandler_arm::isThumbMovt(uint32_t instruction) {
+ return (instruction & 0x8000FBF0) == 0x0000F2C0;
+}
+
+bool ArchHandler_arm::isArmMovw(uint32_t instruction) {
+ return (instruction & 0x0FF00000) == 0x03000000;
+}
+
+bool ArchHandler_arm::isArmMovt(uint32_t instruction) {
+ return (instruction & 0x0FF00000) == 0x03400000;
+}
+
+uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) {
+ assert(isThumbMovw(instruction) || isThumbMovt(instruction));
+ uint32_t i = ((instruction & 0x00000400) >> 10);
+ uint32_t imm4 = (instruction & 0x0000000F);
+ uint32_t imm3 = ((instruction & 0x70000000) >> 28);
+ uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
+ return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
+}
+
+uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) {
+ assert(isArmMovw(instruction) || isArmMovt(instruction));
+ uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
+ uint32_t imm12 = (instruction & 0x00000FFF);
+ return (imm4 << 12) | imm12;
+}
+
+uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) {
+ assert(isThumbMovw(instr) || isThumbMovt(instr));
+ uint32_t imm4 = (word & 0xF000) >> 12;
+ uint32_t i = (word & 0x0800) >> 11;
+ uint32_t imm3 = (word & 0x0700) >> 8;
+ uint32_t imm8 = word & 0x00FF;
+ return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
+}
+
+uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) {
+ assert(isArmMovw(instr) || isArmMovt(instr));
+ uint32_t imm4 = (word & 0xF000) >> 12;
+ uint32_t imm12 = word & 0x0FFF;
+ return (instr & 0xFFF0F000) | (imm4 << 16) | imm12;
+}
+
+uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) {
+ // The assembler often adds one to the address of a thumb function.
+ // We need to undo that so it does not look like an addend.
+ if (value & 1) {
+ if (isa<DefinedAtom>(target)) {
+ const MachODefinedAtom *machoTarget =
+ reinterpret_cast<const MachODefinedAtom *>(target);
+ if (machoTarget->isThumb())
+ value &= -2; // mask off thumb-bit
+ }
+ }
+ return value;
+}
+
+llvm::Error ArchHandler_arm::getReferenceInfo(
+ const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
+ const lld::Atom **target, Reference::Addend *addend) {
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ uint64_t targetAddress;
+ uint32_t instruction = *(const ulittle32_t *)fixupContent;
+ int32_t displacement;
+ switch (relocPattern(reloc)) {
+ case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4:
+ // ex: bl _foo (and _foo is undefined)
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ // Instruction contains branch to addend.
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
+ *addend = fixupAddress + 4 + displacement;
+ return llvm::Error::success();
+ case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
+ // ex: bl _foo (and _foo is defined)
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
+ targetAddress = fixupAddress + 4 + displacement;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4:
+ // ex: bl _foo+4 (and _foo is defined)
+ if ((instruction & 0xD000F800) == 0x9000F000)
+ *kind = thumb_b22;
+ else
+ *kind = thumb_bl22;
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
+ targetAddress = fixupAddress + 4 + displacement;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ // reloc.value is target atom's address. Instruction contains branch
+ // to atom+addend.
+ *addend += (targetAddress - reloc.value);
+ return llvm::Error::success();
+ case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
+ // ex: bl _foo (and _foo is undefined)
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ // Instruction contains branch to addend.
+ displacement = getDisplacementFromArmBranch(instruction);
+ *addend = fixupAddress + 8 + displacement;
+ return llvm::Error::success();
+ case ARM_RELOC_BR24 | rPcRel | rLength4:
+ // ex: bl _foo (and _foo is defined)
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
+ displacement = getDisplacementFromArmBranch(instruction);
+ targetAddress = fixupAddress + 8 + displacement;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4:
+ // ex: bl _foo+4 (and _foo is defined)
+ if (((instruction & 0x0F000000) == 0x0A000000)
+ && ((instruction & 0xF0000000) != 0xF0000000))
+ *kind = arm_b24;
+ else
+ *kind = arm_bl24;
+ displacement = getDisplacementFromArmBranch(instruction);
+ targetAddress = fixupAddress + 8 + displacement;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ // reloc.value is target atom's address. Instruction contains branch
+ // to atom+addend.
+ *addend += (targetAddress - reloc.value);
+ return llvm::Error::success();
+ case ARM_RELOC_VANILLA | rExtern | rLength4:
+ // ex: .long _foo (and _foo is undefined)
+ *kind = pointer32;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = instruction;
+ return llvm::Error::success();
+ case ARM_RELOC_VANILLA | rLength4:
+ // ex: .long _foo (and _foo is defined)
+ *kind = pointer32;
+ if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend))
+ return ec;
+ *addend = clearThumbBit((uint32_t) * addend, *target);
+ return llvm::Error::success();
+ case ARM_RELOC_VANILLA | rScattered | rLength4:
+ // ex: .long _foo+a (and _foo is defined)
+ *kind = pointer32;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ *addend += (clearThumbBit(instruction, *target) - reloc.value);
+ return llvm::Error::success();
+ default:
+ return llvm::make_error<GenericError>("unsupported arm relocation type");
+ }
+ return llvm::Error::success();
+}
+
+llvm::Error
+ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig,
+ bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddr,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) {
+ bool pointerDiff = false;
+ bool funcRel;
+ bool top;
+ bool thumbReloc;
+ switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
+ case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLenThmbLo):
+ // ex: movw r1, :lower16:(_x-L1) [thumb mode]
+ *kind = thumb_movw_funcRel;
+ funcRel = true;
+ top = false;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLenThmbHi):
+ // ex: movt r1, :upper16:(_x-L1) [thumb mode]
+ *kind = thumb_movt_funcRel;
+ funcRel = true;
+ top = true;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLenArmLo):
+ // ex: movw r1, :lower16:(_x-L1) [arm mode]
+ *kind = arm_movw_funcRel;
+ funcRel = true;
+ top = false;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLenArmHi):
+ // ex: movt r1, :upper16:(_x-L1) [arm mode]
+ *kind = arm_movt_funcRel;
+ funcRel = true;
+ top = true;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rLenThmbLo) << 16 |
+ ARM_RELOC_PAIR | rLenThmbLo):
+ // ex: movw r1, :lower16:_x [thumb mode]
+ *kind = thumb_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rLenThmbHi) << 16 |
+ ARM_RELOC_PAIR | rLenThmbHi):
+ // ex: movt r1, :upper16:_x [thumb mode]
+ *kind = thumb_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rLenArmLo) << 16 |
+ ARM_RELOC_PAIR | rLenArmLo):
+ // ex: movw r1, :lower16:_x [arm mode]
+ *kind = arm_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rLenArmHi) << 16 |
+ ARM_RELOC_PAIR | rLenArmHi):
+ // ex: movt r1, :upper16:_x [arm mode]
+ *kind = arm_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 |
+ ARM_RELOC_PAIR | rLenThmbLo):
+ // ex: movw r1, :lower16:_x+a [thumb mode]
+ *kind = thumb_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 |
+ ARM_RELOC_PAIR | rLenThmbHi):
+ // ex: movt r1, :upper16:_x+a [thumb mode]
+ *kind = thumb_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 |
+ ARM_RELOC_PAIR | rLenArmLo):
+ // ex: movw r1, :lower16:_x+a [arm mode]
+ *kind = arm_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 |
+ ARM_RELOC_PAIR | rLenArmHi):
+ // ex: movt r1, :upper16:_x+a [arm mode]
+ *kind = arm_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 |
+ ARM_RELOC_PAIR | rLenThmbLo):
+ // ex: movw r1, :lower16:_undef [thumb mode]
+ *kind = thumb_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 |
+ ARM_RELOC_PAIR | rLenThmbHi):
+ // ex: movt r1, :upper16:_undef [thumb mode]
+ *kind = thumb_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = true;
+ break;
+ case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 |
+ ARM_RELOC_PAIR | rLenArmLo):
+ // ex: movw r1, :lower16:_undef [arm mode]
+ *kind = arm_movw;
+ funcRel = false;
+ top = false;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 |
+ ARM_RELOC_PAIR | rLenArmHi):
+ // ex: movt r1, :upper16:_undef [arm mode]
+ *kind = arm_movt;
+ funcRel = false;
+ top = true;
+ thumbReloc = false;
+ break;
+ case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLength4):
+ case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
+ ARM_RELOC_PAIR | rScattered | rLength4):
+ // ex: .long _foo - .
+ pointerDiff = true;
+ break;
+ default:
+ return llvm::make_error<GenericError>("unsupported arm relocation pair");
+ }
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ uint32_t instruction = *(const ulittle32_t *)fixupContent;
+ uint32_t value;
+ uint32_t fromAddress;
+ uint32_t toAddress;
+ uint16_t instruction16;
+ uint16_t other16;
+ const lld::Atom *fromTarget;
+ Reference::Addend offsetInTo;
+ Reference::Addend offsetInFrom;
+ if (pointerDiff) {
+ toAddress = reloc1.value;
+ fromAddress = reloc2.value;
+ if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
+ return ec;
+ if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
+ return ec;
+ if (scatterable && (fromTarget != inAtom))
+ return llvm::make_error<GenericError>(
+ "SECTDIFF relocation where subtrahend label is not in atom");
+ *kind = delta32;
+ value = clearThumbBit(instruction, *target);
+ *addend = (int32_t)(value - (toAddress - fixupAddress));
+ } else if (funcRel) {
+ toAddress = reloc1.value;
+ fromAddress = reloc2.value;
+ if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
+ return ec;
+ if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
+ return ec;
+ if (fromTarget != inAtom)
+ return llvm::make_error<GenericError>("ARM_RELOC_HALF_SECTDIFF relocation"
+ " where subtrahend label is not in atom");
+ other16 = (reloc2.offset & 0xFFFF);
+ if (thumbReloc) {
+ if (top) {
+ if (!isThumbMovt(instruction))
+ return llvm::make_error<GenericError>("expected movt instruction");
+ }
+ else {
+ if (!isThumbMovw(instruction))
+ return llvm::make_error<GenericError>("expected movw instruction");
+ }
+ instruction16 = getWordFromThumbMov(instruction);
+ }
+ else {
+ if (top) {
+ if (!isArmMovt(instruction))
+ return llvm::make_error<GenericError>("expected movt instruction");
+ }
+ else {
+ if (!isArmMovw(instruction))
+ return llvm::make_error<GenericError>("expected movw instruction");
+ }
+ instruction16 = getWordFromArmMov(instruction);
+ }
+ if (top)
+ value = (instruction16 << 16) | other16;
+ else
+ value = (other16 << 16) | instruction16;
+ value = clearThumbBit(value, *target);
+ int64_t ta = (int64_t) value - (toAddress - fromAddress);
+ *addend = ta - offsetInFrom;
+ return llvm::Error::success();
+ } else {
+ uint32_t sectIndex;
+ if (thumbReloc) {
+ if (top) {
+ if (!isThumbMovt(instruction))
+ return llvm::make_error<GenericError>("expected movt instruction");
+ }
+ else {
+ if (!isThumbMovw(instruction))
+ return llvm::make_error<GenericError>("expected movw instruction");
+ }
+ instruction16 = getWordFromThumbMov(instruction);
+ }
+ else {
+ if (top) {
+ if (!isArmMovt(instruction))
+ return llvm::make_error<GenericError>("expected movt instruction");
+ }
+ else {
+ if (!isArmMovw(instruction))
+ return llvm::make_error<GenericError>("expected movw instruction");
+ }
+ instruction16 = getWordFromArmMov(instruction);
+ }
+ other16 = (reloc2.offset & 0xFFFF);
+ if (top)
+ value = (instruction16 << 16) | other16;
+ else
+ value = (other16 << 16) | instruction16;
+ if (reloc1.isExtern) {
+ if (auto ec = atomFromSymbolIndex(reloc1.symbol, target))
+ return ec;
+ *addend = value;
+ } else {
+ if (reloc1.scattered) {
+ toAddress = reloc1.value;
+ sectIndex = 0;
+ } else {
+ toAddress = value;
+ sectIndex = reloc1.symbol;
+ }
+ if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo))
+ return ec;
+ *addend = value - toAddress;
+ }
+ }
+
+ return llvm::Error::success();
+}
+
+void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress,
+ bool &thumbMode, bool targetIsThumb) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::ARM);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ int32_t displacement;
+ uint16_t value16;
+ uint32_t value32;
+ switch (static_cast<ArmKind>(ref.kindValue())) {
+ case modeThumbCode:
+ thumbMode = true;
+ break;
+ case modeArmCode:
+ thumbMode = false;
+ break;
+ case modeData:
+ break;
+ case thumb_b22:
+ case thumb_bl22:
+ assert(thumbMode);
+ displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
+ value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
+ displacement, targetIsThumb);
+ *loc32 = value32;
+ break;
+ case thumb_movw:
+ assert(thumbMode);
+ value16 = (targetAddress + ref.addend()) & 0xFFFF;
+ if (targetIsThumb)
+ value16 |= 1;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movt:
+ assert(thumbMode);
+ value16 = (targetAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movw_funcRel:
+ assert(thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
+ if (targetIsThumb)
+ value16 |= 1;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movt_funcRel:
+ assert(thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case arm_b24:
+ case arm_bl24:
+ assert(!thumbMode);
+ displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
+ value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
+ *loc32 = value32;
+ break;
+ case arm_movw:
+ assert(!thumbMode);
+ value16 = (targetAddress + ref.addend()) & 0xFFFF;
+ if (targetIsThumb)
+ value16 |= 1;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movt:
+ assert(!thumbMode);
+ value16 = (targetAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movw_funcRel:
+ assert(!thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
+ if (targetIsThumb)
+ value16 |= 1;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movt_funcRel:
+ assert(!thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case pointer32:
+ if (targetIsThumb)
+ *loc32 = targetAddress + ref.addend() + 1;
+ else
+ *loc32 = targetAddress + ref.addend();
+ break;
+ case delta32:
+ if (targetIsThumb)
+ *loc32 = targetAddress - fixupAddress + ref.addend() + 1;
+ else
+ *loc32 = targetAddress - fixupAddress + ref.addend();
+ break;
+ case lazyPointer:
+ // do nothing
+ break;
+ case lazyImmediateLocation:
+ *loc32 = ref.addend();
+ break;
+ case invalid:
+ llvm_unreachable("invalid ARM Reference Kind");
+ break;
+ }
+}
+
+void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom,
+ bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
+ // Copy raw bytes.
+ std::copy(atom.rawContent().begin(), atom.rawContent().end(),
+ atomContentBuffer.begin());
+ // Apply fix-ups.
+ bool thumbMode = false;
+ for (const Reference *ref : atom) {
+ uint32_t offset = ref->offsetInAtom();
+ const Atom *target = ref->target();
+ uint64_t targetAddress = 0;
+ bool targetIsThumb = false;
+ if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) {
+ targetAddress = findAddress(*target);
+ targetIsThumb = isThumbFunction(*defTarg);
+ }
+ uint64_t atomAddress = findAddress(atom);
+ uint64_t fixupAddress = atomAddress + offset;
+ if (relocatable) {
+ applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, thumbMode,
+ targetIsThumb);
+ } else {
+ applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, thumbMode, targetIsThumb);
+ }
+ }
+}
+
+bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) {
+ // Undefined symbols are referenced via external relocations.
+ if (isa<UndefinedAtom>(&target))
+ return true;
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
+ switch (defAtom->merge()) {
+ case DefinedAtom::mergeAsTentative:
+ // Tentative definitions are referenced via external relocations.
+ return true;
+ case DefinedAtom::mergeAsWeak:
+ case DefinedAtom::mergeAsWeakAndAddressUsed:
+ // Global weak-defs are referenced via external relocations.
+ return (defAtom->scope() == DefinedAtom::scopeGlobal);
+ default:
+ break;
+ }
+ }
+ // Everything else is reference via an internal relocation.
+ return false;
+}
+
+void ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress,
+ bool &thumbMode,
+ bool targetIsThumb) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::ARM);
+ bool useExternalReloc = useExternalRelocationTo(*ref.target());
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ int32_t displacement;
+ uint16_t value16;
+ uint32_t value32;
+ bool targetIsUndef = isa<UndefinedAtom>(ref.target());
+ switch (static_cast<ArmKind>(ref.kindValue())) {
+ case modeThumbCode:
+ thumbMode = true;
+ break;
+ case modeArmCode:
+ thumbMode = false;
+ break;
+ case modeData:
+ break;
+ case thumb_b22:
+ case thumb_bl22:
+ assert(thumbMode);
+ if (useExternalReloc)
+ displacement = (ref.addend() - (fixupAddress + 4));
+ else
+ displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
+ value32 = setDisplacementInThumbBranch(*loc32, fixupAddress,
+ displacement,
+ targetIsUndef || targetIsThumb);
+ *loc32 = value32;
+ break;
+ case thumb_movw:
+ assert(thumbMode);
+ if (useExternalReloc)
+ value16 = ref.addend() & 0xFFFF;
+ else
+ value16 = (targetAddress + ref.addend()) & 0xFFFF;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movt:
+ assert(thumbMode);
+ if (useExternalReloc)
+ value16 = ref.addend() >> 16;
+ else
+ value16 = (targetAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movw_funcRel:
+ assert(thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case thumb_movt_funcRel:
+ assert(thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromThumbMov(*loc32, value16);
+ break;
+ case arm_b24:
+ case arm_bl24:
+ assert(!thumbMode);
+ if (useExternalReloc)
+ displacement = (ref.addend() - (fixupAddress + 8));
+ else
+ displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
+ value32 = setDisplacementInArmBranch(*loc32, displacement,
+ targetIsThumb);
+ *loc32 = value32;
+ break;
+ case arm_movw:
+ assert(!thumbMode);
+ if (useExternalReloc)
+ value16 = ref.addend() & 0xFFFF;
+ else
+ value16 = (targetAddress + ref.addend()) & 0xFFFF;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movt:
+ assert(!thumbMode);
+ if (useExternalReloc)
+ value16 = ref.addend() >> 16;
+ else
+ value16 = (targetAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movw_funcRel:
+ assert(!thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case arm_movt_funcRel:
+ assert(!thumbMode);
+ value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16;
+ *loc32 = setWordFromArmMov(*loc32, value16);
+ break;
+ case pointer32:
+ *loc32 = targetAddress + ref.addend();
+ break;
+ case delta32:
+ *loc32 = targetAddress - fixupAddress + ref.addend();
+ break;
+ case lazyPointer:
+ case lazyImmediateLocation:
+ // do nothing
+ break;
+ case invalid:
+ llvm_unreachable("invalid ARM Reference Kind");
+ break;
+ }
+}
+
+void ArchHandler_arm::appendSectionRelocations(
+ const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::ARM);
+ uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
+ bool useExternalReloc = useExternalRelocationTo(*ref.target());
+ uint32_t targetAtomAddress;
+ uint32_t fromAtomAddress;
+ uint16_t other16;
+ switch (static_cast<ArmKind>(ref.kindValue())) {
+ case modeThumbCode:
+ case modeArmCode:
+ case modeData:
+ // Do nothing.
+ break;
+ case thumb_b22:
+ case thumb_bl22:
+ if (useExternalReloc) {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4);
+ } else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_THUMB_RELOC_BR22 | rPcRel | rLength4);
+ }
+ break;
+ case thumb_movw:
+ if (useExternalReloc) {
+ other16 = ref.addend() >> 16;
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_HALF | rExtern | rLenThmbLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbLo);
+ } else {
+ targetAtomAddress = addressForAtom(*ref.target());
+ if (ref.addend() != 0) {
+ other16 = (targetAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF | rScattered | rLenThmbLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbLo);
+ } else {
+ other16 = (targetAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_HALF | rLenThmbLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbLo);
+ }
+ }
+ break;
+ case thumb_movt:
+ if (useExternalReloc) {
+ other16 = ref.addend() & 0xFFFF;
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_HALF | rExtern | rLenThmbHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbHi);
+ } else {
+ targetAtomAddress = addressForAtom(*ref.target());
+ if (ref.addend() != 0) {
+ other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF | rScattered | rLenThmbHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbHi);
+ } else {
+ other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_HALF | rLenThmbHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenThmbHi);
+ }
+ }
+ break;
+ case thumb_movw_funcRel:
+ fromAtomAddress = addressForAtom(atom);
+ targetAtomAddress = addressForAtom(*ref.target());
+ other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo);
+ appendReloc(relocs, other16, 0, fromAtomAddress,
+ ARM_RELOC_PAIR | rScattered | rLenThmbLo);
+ break;
+ case thumb_movt_funcRel:
+ fromAtomAddress = addressForAtom(atom);
+ targetAtomAddress = addressForAtom(*ref.target());
+ other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi);
+ appendReloc(relocs, other16, 0, fromAtomAddress,
+ ARM_RELOC_PAIR | rScattered | rLenThmbHi);
+ break;
+ case arm_b24:
+ case arm_bl24:
+ if (useExternalReloc) {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_BR24 | rExtern | rPcRel | rLength4);
+ } else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ ARM_RELOC_BR24 | rScattered | rPcRel | rLength4);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_BR24 | rPcRel | rLength4);
+ }
+ break;
+ case arm_movw:
+ if (useExternalReloc) {
+ other16 = ref.addend() >> 16;
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_HALF | rExtern | rLenArmLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmLo);
+ } else {
+ targetAtomAddress = addressForAtom(*ref.target());
+ if (ref.addend() != 0) {
+ other16 = (targetAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF | rScattered | rLenArmLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmLo);
+ } else {
+ other16 = (targetAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_HALF | rLenArmLo);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmLo);
+ }
+ }
+ break;
+ case arm_movt:
+ if (useExternalReloc) {
+ other16 = ref.addend() & 0xFFFF;
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_HALF | rExtern | rLenArmHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmHi);
+ } else {
+ targetAtomAddress = addressForAtom(*ref.target());
+ if (ref.addend() != 0) {
+ other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF | rScattered | rLenArmHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmHi);
+ } else {
+ other16 = (targetAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_HALF | rLenArmHi);
+ appendReloc(relocs, other16, 0, 0,
+ ARM_RELOC_PAIR | rLenArmHi);
+ }
+ }
+ break;
+ case arm_movw_funcRel:
+ fromAtomAddress = addressForAtom(atom);
+ targetAtomAddress = addressForAtom(*ref.target());
+ other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo);
+ appendReloc(relocs, other16, 0, fromAtomAddress,
+ ARM_RELOC_PAIR | rScattered | rLenArmLo);
+ break;
+ case arm_movt_funcRel:
+ fromAtomAddress = addressForAtom(atom);
+ targetAtomAddress = addressForAtom(*ref.target());
+ other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF;
+ appendReloc(relocs, sectionOffset, 0, targetAtomAddress,
+ ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi);
+ appendReloc(relocs, other16, 0, fromAtomAddress,
+ ARM_RELOC_PAIR | rScattered | rLenArmHi);
+ break;
+ case pointer32:
+ if (useExternalReloc) {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM_RELOC_VANILLA | rExtern | rLength4);
+ }
+ else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ ARM_RELOC_VANILLA | rScattered | rLength4);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ ARM_RELOC_VANILLA | rLength4);
+ }
+ break;
+ case delta32:
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ ARM_RELOC_SECTDIFF | rScattered | rLength4);
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
+ ref.offsetInAtom(),
+ ARM_RELOC_PAIR | rScattered | rLength4);
+ break;
+ case lazyPointer:
+ case lazyImmediateLocation:
+ // do nothing
+ break;
+ case invalid:
+ llvm_unreachable("invalid ARM Reference Kind");
+ break;
+ }
+}
+
+void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) {
+ if (atom.isThumb()) {
+ atom.addReference(Reference::KindNamespace::mach_o,
+ Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0);
+ }
+}
+
+bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) {
+ for (const Reference *ref : atom) {
+ if (ref->offsetInAtom() != 0)
+ return false;
+ if (ref->kindNamespace() != Reference::KindNamespace::mach_o)
+ continue;
+ assert(ref->kindArch() == Reference::KindArch::ARM);
+ if (ref->kindValue() == modeThumbCode)
+ return true;
+ }
+ return false;
+}
+
+class Thumb2ToArmShimAtom : public SimpleDefinedAtom {
+public:
+ Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName,
+ const DefinedAtom &target)
+ : SimpleDefinedAtom(file) {
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::modeThumbCode, 0, this, 0);
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::delta32, 8, &target, 0);
+ std::string name = std::string(targetName) + "$shim";
+ StringRef tmp(name);
+ _name = tmp.copy(file.allocator());
+ }
+
+ ~Thumb2ToArmShimAtom() override = default;
+
+ StringRef name() const override {
+ return _name;
+ }
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeCode;
+ }
+
+ Alignment alignment() const override { return 4; }
+
+ uint64_t size() const override {
+ return 12;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR_X;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t bytes[] =
+ { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4
+ 0xFF, 0x44, // add ip, pc, ip
+ 0x60, 0x47, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00 }; // .long target - this
+ assert(sizeof(bytes) == size());
+ return llvm::makeArrayRef(bytes, sizeof(bytes));
+ }
+private:
+ StringRef _name;
+};
+
+class ArmToThumbShimAtom : public SimpleDefinedAtom {
+public:
+ ArmToThumbShimAtom(MachOFile &file, StringRef targetName,
+ const DefinedAtom &target)
+ : SimpleDefinedAtom(file) {
+ addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM,
+ ArchHandler_arm::delta32, 12, &target, 0);
+ std::string name = std::string(targetName) + "$shim";
+ StringRef tmp(name);
+ _name = tmp.copy(file.allocator());
+ }
+
+ ~ArmToThumbShimAtom() override = default;
+
+ StringRef name() const override {
+ return _name;
+ }
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeCode;
+ }
+
+ Alignment alignment() const override { return 4; }
+
+ uint64_t size() const override {
+ return 16;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR_X;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t bytes[] =
+ { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4
+ 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip
+ 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip]
+ 0x00, 0x00, 0x00, 0x00 }; // .long target - this
+ assert(sizeof(bytes) == size());
+ return llvm::makeArrayRef(bytes, sizeof(bytes));
+ }
+private:
+ StringRef _name;
+};
+
+const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file,
+ bool thumbToArm,
+ const DefinedAtom &target) {
+ bool isStub = (target.contentType() == DefinedAtom::typeStub);
+ StringRef targetName = isStub ? stubName(target) : target.name();
+ if (thumbToArm)
+ return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target);
+ else
+ return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target);
+}
+
+std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() {
+ return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm());
+}
+
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+namespace lld {
+namespace mach_o {
+
+using llvm::support::ulittle32_t;
+using llvm::support::ulittle64_t;
+
+using llvm::support::little32_t;
+using llvm::support::little64_t;
+
+class ArchHandler_arm64 : public ArchHandler {
+public:
+ ArchHandler_arm64() = default;
+ ~ArchHandler_arm64() override = default;
+
+ const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
+
+ Reference::KindArch kindArch() override {
+ return Reference::KindArch::AArch64;
+ }
+
+ /// Used by GOTPass to locate GOT References
+ bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ switch (ref.kindValue()) {
+ case gotPage21:
+ case gotOffset12:
+ canBypassGOT = true;
+ return true;
+ case delta32ToGOT:
+ case unwindCIEToPersonalityFunction:
+ case imageOffsetGot:
+ canBypassGOT = false;
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /// Used by GOTPass to update GOT References.
+ void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
+ // If GOT slot was instantiated, transform:
+ // gotPage21/gotOffset12 -> page21/offset12scale8
+ // If GOT slot optimized away, transform:
+ // gotPage21/gotOffset12 -> page21/addOffset12
+ assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
+ assert(ref->kindArch() == Reference::KindArch::AArch64);
+ switch (ref->kindValue()) {
+ case gotPage21:
+ const_cast<Reference *>(ref)->setKindValue(page21);
+ break;
+ case gotOffset12:
+ const_cast<Reference *>(ref)->setKindValue(targetNowGOT ?
+ offset12scale8 : addOffset12);
+ break;
+ case delta32ToGOT:
+ const_cast<Reference *>(ref)->setKindValue(delta32);
+ break;
+ case imageOffsetGot:
+ const_cast<Reference *>(ref)->setKindValue(imageOffset);
+ break;
+ default:
+ llvm_unreachable("Not a GOT reference");
+ }
+ }
+
+ const StubInfo &stubInfo() override { return _sStubInfo; }
+
+ bool isCallSite(const Reference &) override;
+ bool isNonCallBranch(const Reference &) override {
+ return false;
+ }
+
+ bool isPointer(const Reference &) override;
+ bool isPairedReloc(const normalized::Relocation &) override;
+
+ bool needsCompactUnwind() override {
+ return true;
+ }
+ Reference::KindValue imageOffsetKind() override {
+ return imageOffset;
+ }
+ Reference::KindValue imageOffsetKindIndirect() override {
+ return imageOffsetGot;
+ }
+
+ Reference::KindValue unwindRefToPersonalityFunctionKind() override {
+ return unwindCIEToPersonalityFunction;
+ }
+
+ Reference::KindValue unwindRefToCIEKind() override {
+ return negDelta32;
+ }
+
+ Reference::KindValue unwindRefToFunctionKind() override {
+ return unwindFDEToFunction;
+ }
+
+ Reference::KindValue unwindRefToEhFrameKind() override {
+ return unwindInfoToEhFrame;
+ }
+
+ Reference::KindValue pointerKind() override {
+ return pointer64;
+ }
+
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
+ uint32_t dwarfCompactUnwindType() override {
+ return 0x03000000;
+ }
+
+ llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+ llvm::Error
+ getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig, bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+
+ bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
+ return (atom->contentType() == DefinedAtom::typeCString);
+ }
+
+ void generateAtomContent(const DefinedAtom &atom, bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
+
+ void appendSectionRelocations(const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) override;
+
+private:
+ static const Registry::KindStrings _sKindStrings[];
+ static const StubInfo _sStubInfo;
+
+ enum Arm64Kind : Reference::KindValue {
+ invalid, /// for error condition
+
+ // Kinds found in mach-o .o files:
+ branch26, /// ex: bl _foo
+ page21, /// ex: adrp x1, _foo@PAGE
+ offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF]
+ offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF]
+ offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF]
+ offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF]
+ offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF]
+ gotPage21, /// ex: adrp x1, _foo@GOTPAGE
+ gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF]
+ tlvPage21, /// ex: adrp x1, _foo@TLVPAGE
+ tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF]
+
+ pointer64, /// ex: .quad _foo
+ delta64, /// ex: .quad _foo - .
+ delta32, /// ex: .long _foo - .
+ negDelta32, /// ex: .long . - _foo
+ pointer64ToGOT, /// ex: .quad _foo@GOT
+ delta32ToGOT, /// ex: .long _foo@GOT - .
+
+ // Kinds introduced by Passes:
+ addOffset12, /// Location contains LDR to change into ADD.
+ lazyPointer, /// Location contains a lazy pointer.
+ lazyImmediateLocation, /// Location contains immediate value used in stub.
+ imageOffset, /// Location contains offset of atom in final image
+ imageOffsetGot, /// Location contains offset of GOT entry for atom in
+ /// final image (typically personality function).
+ unwindCIEToPersonalityFunction, /// Nearly delta32ToGOT, but cannot be
+ /// rematerialized in relocatable object
+ /// (yay for implicit contracts!).
+ unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
+ /// relocatable object (yay for implicit contracts!).
+ unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
+ /// refer to __eh_frame entry.
+ };
+
+ void applyFixupFinal(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress, uint64_t targetAddress,
+ uint64_t inAtomAddress, uint64_t imageBaseAddress,
+ FindAddressForAtom findSectionAddress);
+
+ void applyFixupRelocatable(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress, uint64_t targetAddress,
+ uint64_t inAtomAddress, bool targetUnnamed);
+
+ // Utility functions for inspecting/updating instructions.
+ static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp);
+ static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp);
+ static Arm64Kind offset12KindFromInstruction(uint32_t instr);
+ static uint32_t setImm12(uint32_t instr, uint32_t offset);
+};
+
+const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = {
+ LLD_KIND_STRING_ENTRY(invalid),
+ LLD_KIND_STRING_ENTRY(branch26),
+ LLD_KIND_STRING_ENTRY(page21),
+ LLD_KIND_STRING_ENTRY(offset12),
+ LLD_KIND_STRING_ENTRY(offset12scale2),
+ LLD_KIND_STRING_ENTRY(offset12scale4),
+ LLD_KIND_STRING_ENTRY(offset12scale8),
+ LLD_KIND_STRING_ENTRY(offset12scale16),
+ LLD_KIND_STRING_ENTRY(gotPage21),
+ LLD_KIND_STRING_ENTRY(gotOffset12),
+ LLD_KIND_STRING_ENTRY(tlvPage21),
+ LLD_KIND_STRING_ENTRY(tlvOffset12),
+ LLD_KIND_STRING_ENTRY(pointer64),
+ LLD_KIND_STRING_ENTRY(delta64),
+ LLD_KIND_STRING_ENTRY(delta32),
+ LLD_KIND_STRING_ENTRY(negDelta32),
+ LLD_KIND_STRING_ENTRY(pointer64ToGOT),
+ LLD_KIND_STRING_ENTRY(delta32ToGOT),
+
+ LLD_KIND_STRING_ENTRY(addOffset12),
+ LLD_KIND_STRING_ENTRY(lazyPointer),
+ LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
+ LLD_KIND_STRING_ENTRY(imageOffset),
+ LLD_KIND_STRING_ENTRY(imageOffsetGot),
+ LLD_KIND_STRING_ENTRY(unwindCIEToPersonalityFunction),
+ LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
+ LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
+
+ LLD_KIND_STRING_END
+};
+
+const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = {
+ "dyld_stub_binder",
+
+ // Lazy pointer references
+ { Reference::KindArch::AArch64, pointer64, 0, 0 },
+ { Reference::KindArch::AArch64, lazyPointer, 0, 0 },
+
+ // GOT pointer to dyld_stub_binder
+ { Reference::KindArch::AArch64, pointer64, 0, 0 },
+
+ // arm64 code alignment 2^1
+ 1,
+
+ // Stub size and code
+ 12,
+ { 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page
+ 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff]
+ 0x00, 0x02, 0x1F, 0xD6 }, // BR X16
+ { Reference::KindArch::AArch64, page21, 0, 0 },
+ { true, offset12scale8, 4, 0 },
+
+ // Stub Helper size and code
+ 12,
+ { 0x50, 0x00, 0x00, 0x18, // LDR W16, L0
+ 0x00, 0x00, 0x00, 0x14, // LDR B helperhelper
+ 0x00, 0x00, 0x00, 0x00 }, // L0: .long 0
+ { Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 },
+ { Reference::KindArch::AArch64, branch26, 4, 0 },
+
+ // Stub helper image cache content type
+ DefinedAtom::typeGOT,
+
+ // Stub Helper-Common size and code
+ 24,
+ // Stub helper alignment
+ 2,
+ { 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page
+ 0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff
+ 0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]!
+ 0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page
+ 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff]
+ 0x00, 0x02, 0x1F, 0xD6 }, // BR X16
+ { Reference::KindArch::AArch64, page21, 0, 0 },
+ { true, offset12, 4, 0 },
+ { Reference::KindArch::AArch64, page21, 12, 0 },
+ { true, offset12scale8, 16, 0 }
+};
+
+bool ArchHandler_arm64::isCallSite(const Reference &ref) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ return (ref.kindValue() == branch26);
+}
+
+bool ArchHandler_arm64::isPointer(const Reference &ref) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ Reference::KindValue kind = ref.kindValue();
+ return (kind == pointer64);
+}
+
+bool ArchHandler_arm64::isPairedReloc(const Relocation &r) {
+ return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR));
+}
+
+uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr,
+ int32_t displacement) {
+ assert((displacement <= 134217727) && (displacement > (-134217728)) &&
+ "arm64 branch out of range");
+ return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF);
+}
+
+uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction,
+ int64_t displacement) {
+ assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) &&
+ "arm64 ADRP out of range");
+ assert(((instruction & 0x9F000000) == 0x90000000) &&
+ "reloc not on ADRP instruction");
+ uint32_t immhi = (displacement >> 9) & (0x00FFFFE0);
+ uint32_t immlo = (displacement << 17) & (0x60000000);
+ return (instruction & 0x9F00001F) | immlo | immhi;
+}
+
+ArchHandler_arm64::Arm64Kind
+ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) {
+ if (instruction & 0x08000000) {
+ switch ((instruction >> 30) & 0x3) {
+ case 0:
+ if ((instruction & 0x04800000) == 0x04800000)
+ return offset12scale16;
+ return offset12;
+ case 1:
+ return offset12scale2;
+ case 2:
+ return offset12scale4;
+ case 3:
+ return offset12scale8;
+ }
+ }
+ return offset12;
+}
+
+uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) {
+ assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range");
+ uint32_t imm12 = offset << 10;
+ return (instruction & 0xFFC003FF) | imm12;
+}
+
+llvm::Error ArchHandler_arm64::getReferenceInfo(
+ const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool isBig,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
+ const lld::Atom **target, Reference::Addend *addend) {
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ switch (relocPattern(reloc)) {
+ case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4:
+ // ex: bl _foo
+ *kind = branch26;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4:
+ // ex: adrp x1, _foo@PAGE
+ *kind = page21;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4:
+ // ex: ldr x0, [x1, _foo@PAGEOFF]
+ *kind = offset12KindFromInstruction(*(const little32_t *)fixupContent);
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
+ // ex: adrp x1, _foo@GOTPAGE
+ *kind = gotPage21;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4:
+ // ex: ldr x0, [x1, _foo@GOTPAGEOFF]
+ *kind = gotOffset12;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
+ // ex: adrp x1, _foo@TLVPAGE
+ *kind = tlvPage21;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4:
+ // ex: ldr x0, [x1, _foo@TLVPAGEOFF]
+ *kind = tlvOffset12;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_UNSIGNED | rExtern | rLength8:
+ // ex: .quad _foo + N
+ *kind = pointer64;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = *(const little64_t *)fixupContent;
+ return llvm::Error::success();
+ case ARM64_RELOC_UNSIGNED | rLength8:
+ // ex: .quad Lfoo + N
+ *kind = pointer64;
+ return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent,
+ target, addend);
+ case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8:
+ // ex: .quad _foo@GOT
+ *kind = pointer64ToGOT;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4:
+ // ex: .long _foo@GOT - .
+
+ // If we are in an .eh_frame section, then the kind of the relocation should
+ // not be delta32ToGOT. It may instead be unwindCIEToPersonalityFunction.
+ if (inAtom->contentType() == DefinedAtom::typeCFI)
+ *kind = unwindCIEToPersonalityFunction;
+ else
+ *kind = delta32ToGOT;
+
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = 0;
+ return llvm::Error::success();
+ default:
+ return llvm::make_error<GenericError>("unsupported arm64 relocation type");
+ }
+}
+
+llvm::Error ArchHandler_arm64::getPairReferenceInfo(
+ const normalized::Relocation &reloc1, const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress,
+ bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind,
+ const lld::Atom **target, Reference::Addend *addend) {
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
+ case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
+ ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4):
+ // ex: bl _foo+8
+ *kind = branch26;
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ *addend = reloc1.symbol;
+ return llvm::Error::success();
+ case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
+ ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4):
+ // ex: adrp x1, _foo@PAGE
+ *kind = page21;
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ *addend = reloc1.symbol;
+ return llvm::Error::success();
+ case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
+ ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): {
+ // ex: ldr w0, [x1, _foo@PAGEOFF]
+ uint32_t cont32 = (int32_t)*(const little32_t *)fixupContent;
+ *kind = offset12KindFromInstruction(cont32);
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ *addend = reloc1.symbol;
+ return llvm::Error::success();
+ }
+ case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
+ ARM64_RELOC_UNSIGNED | rExtern | rLength8):
+ // ex: .quad _foo - .
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+
+ // If we are in an .eh_frame section, then the kind of the relocation should
+ // not be delta64. It may instead be unwindFDEToFunction.
+ if (inAtom->contentType() == DefinedAtom::typeCFI)
+ *kind = unwindFDEToFunction;
+ else
+ *kind = delta64;
+
+ // The offsets of the 2 relocations must match
+ if (reloc1.offset != reloc2.offset)
+ return llvm::make_error<GenericError>(
+ "paired relocs must have the same offset");
+ *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom;
+ return llvm::Error::success();
+ case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
+ ARM64_RELOC_UNSIGNED | rExtern | rLength4):
+ // ex: .quad _foo - .
+ *kind = delta32;
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom;
+ return llvm::Error::success();
+ default:
+ return llvm::make_error<GenericError>("unsupported arm64 relocation pair");
+ }
+}
+
+void ArchHandler_arm64::generateAtomContent(
+ const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
+ // Copy raw bytes.
+ std::copy(atom.rawContent().begin(), atom.rawContent().end(),
+ atomContentBuffer.begin());
+ // Apply fix-ups.
+#ifndef NDEBUG
+ if (atom.begin() != atom.end()) {
+ DEBUG_WITH_TYPE("atom-content", llvm::dbgs()
+ << "Applying fixups to atom:\n"
+ << " address="
+ << llvm::format(" 0x%09lX", &atom)
+ << ", file=#"
+ << atom.file().ordinal()
+ << ", atom=#"
+ << atom.ordinal()
+ << ", name="
+ << atom.name()
+ << ", type="
+ << atom.contentType()
+ << "\n");
+ }
+#endif
+ for (const Reference *ref : atom) {
+ uint32_t offset = ref->offsetInAtom();
+ const Atom *target = ref->target();
+ bool targetUnnamed = target->name().empty();
+ uint64_t targetAddress = 0;
+ if (isa<DefinedAtom>(target))
+ targetAddress = findAddress(*target);
+ uint64_t atomAddress = findAddress(atom);
+ uint64_t fixupAddress = atomAddress + offset;
+ if (relocatable) {
+ applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, targetUnnamed);
+ } else {
+ applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, imageBaseAddress,
+ findSectionAddress);
+ }
+ }
+}
+
+void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress,
+ uint64_t imageBaseAddress,
+ FindAddressForAtom findSectionAddress) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
+ int32_t displacement;
+ uint32_t instruction;
+ uint32_t value32;
+ uint32_t value64;
+ switch (static_cast<Arm64Kind>(ref.kindValue())) {
+ case branch26:
+ displacement = (targetAddress - fixupAddress) + ref.addend();
+ *loc32 = setDisplacementInBranch26(*loc32, displacement);
+ return;
+ case page21:
+ case gotPage21:
+ case tlvPage21:
+ displacement =
+ ((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096));
+ *loc32 = setDisplacementInADRP(*loc32, displacement);
+ return;
+ case offset12:
+ case gotOffset12:
+ case tlvOffset12:
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ *loc32 = setImm12(*loc32, displacement);
+ return;
+ case offset12scale2:
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ assert(((displacement & 0x1) == 0) &&
+ "scaled imm12 not accessing 2-byte aligneds");
+ *loc32 = setImm12(*loc32, displacement >> 1);
+ return;
+ case offset12scale4:
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ assert(((displacement & 0x3) == 0) &&
+ "scaled imm12 not accessing 4-byte aligned");
+ *loc32 = setImm12(*loc32, displacement >> 2);
+ return;
+ case offset12scale8:
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ assert(((displacement & 0x7) == 0) &&
+ "scaled imm12 not accessing 8-byte aligned");
+ *loc32 = setImm12(*loc32, displacement >> 3);
+ return;
+ case offset12scale16:
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ assert(((displacement & 0xF) == 0) &&
+ "scaled imm12 not accessing 16-byte aligned");
+ *loc32 = setImm12(*loc32, displacement >> 4);
+ return;
+ case addOffset12:
+ instruction = *loc32;
+ assert(((instruction & 0xFFC00000) == 0xF9400000) &&
+ "GOT reloc is not an LDR instruction");
+ displacement = (targetAddress + ref.addend()) & 0x00000FFF;
+ value32 = 0x91000000 | (instruction & 0x000003FF);
+ instruction = setImm12(value32, displacement);
+ *loc32 = instruction;
+ return;
+ case pointer64:
+ case pointer64ToGOT:
+ *loc64 = targetAddress + ref.addend();
+ return;
+ case delta64:
+ case unwindFDEToFunction:
+ *loc64 = (targetAddress - fixupAddress) + ref.addend();
+ return;
+ case delta32:
+ case delta32ToGOT:
+ case unwindCIEToPersonalityFunction:
+ *loc32 = (targetAddress - fixupAddress) + ref.addend();
+ return;
+ case negDelta32:
+ *loc32 = fixupAddress - targetAddress + ref.addend();
+ return;
+ case lazyPointer:
+ // Do nothing
+ return;
+ case lazyImmediateLocation:
+ *loc32 = ref.addend();
+ return;
+ case imageOffset:
+ *loc32 = (targetAddress - imageBaseAddress) + ref.addend();
+ return;
+ case imageOffsetGot:
+ llvm_unreachable("imageOffsetGot should have been changed to imageOffset");
+ break;
+ case unwindInfoToEhFrame:
+ value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
+ assert(value64 < 0xffffffU && "offset in __eh_frame too large");
+ *loc32 = (*loc32 & 0xff000000U) | value64;
+ return;
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("invalid arm64 Reference Kind");
+}
+
+void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref,
+ uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress,
+ bool targetUnnamed) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
+ switch (static_cast<Arm64Kind>(ref.kindValue())) {
+ case branch26:
+ *loc32 = setDisplacementInBranch26(*loc32, 0);
+ return;
+ case page21:
+ case gotPage21:
+ case tlvPage21:
+ *loc32 = setDisplacementInADRP(*loc32, 0);
+ return;
+ case offset12:
+ case offset12scale2:
+ case offset12scale4:
+ case offset12scale8:
+ case offset12scale16:
+ case gotOffset12:
+ case tlvOffset12:
+ *loc32 = setImm12(*loc32, 0);
+ return;
+ case pointer64:
+ if (targetUnnamed)
+ *loc64 = targetAddress + ref.addend();
+ else
+ *loc64 = ref.addend();
+ return;
+ case delta64:
+ *loc64 = ref.addend() + inAtomAddress - fixupAddress;
+ return;
+ case unwindFDEToFunction:
+ // We don't emit unwindFDEToFunction in -r mode as they are implicitly
+ // generated from the data in the __eh_frame section. So here we need
+ // to use the targetAddress so that we can generate the full relocation
+ // when we parse again later.
+ *loc64 = targetAddress - fixupAddress;
+ return;
+ case delta32:
+ *loc32 = ref.addend() + inAtomAddress - fixupAddress;
+ return;
+ case negDelta32:
+ // We don't emit negDelta32 in -r mode as they are implicitly
+ // generated from the data in the __eh_frame section. So here we need
+ // to use the targetAddress so that we can generate the full relocation
+ // when we parse again later.
+ *loc32 = fixupAddress - targetAddress + ref.addend();
+ return;
+ case pointer64ToGOT:
+ *loc64 = 0;
+ return;
+ case delta32ToGOT:
+ *loc32 = inAtomAddress - fixupAddress;
+ return;
+ case unwindCIEToPersonalityFunction:
+ // We don't emit unwindCIEToPersonalityFunction in -r mode as they are
+ // implicitly generated from the data in the __eh_frame section. So here we
+ // need to use the targetAddress so that we can generate the full relocation
+ // when we parse again later.
+ *loc32 = targetAddress - fixupAddress;
+ return;
+ case addOffset12:
+ llvm_unreachable("lazy reference kind implies GOT pass was run");
+ case lazyPointer:
+ case lazyImmediateLocation:
+ llvm_unreachable("lazy reference kind implies Stubs pass was run");
+ case imageOffset:
+ case imageOffsetGot:
+ case unwindInfoToEhFrame:
+ llvm_unreachable("fixup implies __unwind_info");
+ return;
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("unknown arm64 Reference Kind");
+}
+
+void ArchHandler_arm64::appendSectionRelocations(
+ const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom, normalized::Relocations &relocs) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::AArch64);
+ uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
+ switch (static_cast<Arm64Kind>(ref.kindValue())) {
+ case branch26:
+ if (ref.addend()) {
+ appendReloc(relocs, sectionOffset, ref.addend(), 0,
+ ARM64_RELOC_ADDEND | rLength4);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
+ } else {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4);
+ }
+ return;
+ case page21:
+ if (ref.addend()) {
+ appendReloc(relocs, sectionOffset, ref.addend(), 0,
+ ARM64_RELOC_ADDEND | rLength4);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
+ } else {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4);
+ }
+ return;
+ case offset12:
+ case offset12scale2:
+ case offset12scale4:
+ case offset12scale8:
+ case offset12scale16:
+ if (ref.addend()) {
+ appendReloc(relocs, sectionOffset, ref.addend(), 0,
+ ARM64_RELOC_ADDEND | rLength4);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
+ } else {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_PAGEOFF12 | rExtern | rLength4);
+ }
+ return;
+ case gotPage21:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
+ return;
+ case gotOffset12:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4);
+ return;
+ case tlvPage21:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4);
+ return;
+ case tlvOffset12:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4);
+ return;
+ case pointer64:
+ if (ref.target()->name().empty())
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_UNSIGNED | rLength8);
+ else
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_UNSIGNED | rExtern | rLength8);
+ return;
+ case delta64:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ ARM64_RELOC_SUBTRACTOR | rExtern | rLength8);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_UNSIGNED | rExtern | rLength8);
+ return;
+ case delta32:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 );
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_UNSIGNED | rExtern | rLength4 );
+ return;
+ case pointer64ToGOT:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8);
+ return;
+ case delta32ToGOT:
+ assert(ref.addend() == 0);
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4);
+ return;
+ case addOffset12:
+ llvm_unreachable("lazy reference kind implies GOT pass was run");
+ case lazyPointer:
+ case lazyImmediateLocation:
+ llvm_unreachable("lazy reference kind implies Stubs pass was run");
+ case imageOffset:
+ case imageOffsetGot:
+ llvm_unreachable("deltas from mach_header can only be in final images");
+ case unwindCIEToPersonalityFunction:
+ case unwindFDEToFunction:
+ case unwindInfoToEhFrame:
+ case negDelta32:
+ // Do nothing.
+ return;
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("unknown arm64 Reference Kind");
+}
+
+std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm64() {
+ return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm64());
+}
+
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+namespace lld {
+namespace mach_o {
+
+using llvm::support::ulittle16_t;
+using llvm::support::ulittle32_t;
+
+using llvm::support::little16_t;
+using llvm::support::little32_t;
+
+class ArchHandler_x86 : public ArchHandler {
+public:
+ ArchHandler_x86() = default;
+ ~ArchHandler_x86() override = default;
+
+ const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
+
+ Reference::KindArch kindArch() override { return Reference::KindArch::x86; }
+
+ const StubInfo &stubInfo() override { return _sStubInfo; }
+ bool isCallSite(const Reference &) override;
+ bool isNonCallBranch(const Reference &) override {
+ return false;
+ }
+
+ bool isPointer(const Reference &) override;
+ bool isPairedReloc(const normalized::Relocation &) override;
+
+ bool needsCompactUnwind() override {
+ return false;
+ }
+
+ Reference::KindValue imageOffsetKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue imageOffsetKindIndirect() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToPersonalityFunctionKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue unwindRefToCIEKind() override {
+ return negDelta32;
+ }
+
+ Reference::KindValue unwindRefToFunctionKind() override{
+ return delta32;
+ }
+
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
+ Reference::KindValue unwindRefToEhFrameKind() override {
+ return invalid;
+ }
+
+ Reference::KindValue pointerKind() override {
+ return invalid;
+ }
+
+ uint32_t dwarfCompactUnwindType() override {
+ return 0x04000000U;
+ }
+
+ llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+ llvm::Error
+ getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap, bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+
+ void generateAtomContent(const DefinedAtom &atom, bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
+
+ void appendSectionRelocations(const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) override;
+
+ bool isDataInCodeTransition(Reference::KindValue refKind) override {
+ return refKind == modeCode || refKind == modeData;
+ }
+
+ Reference::KindValue dataInCodeTransitionStart(
+ const MachODefinedAtom &atom) override {
+ return modeData;
+ }
+
+ Reference::KindValue dataInCodeTransitionEnd(
+ const MachODefinedAtom &atom) override {
+ return modeCode;
+ }
+
+private:
+ static const Registry::KindStrings _sKindStrings[];
+ static const StubInfo _sStubInfo;
+
+ enum X86Kind : Reference::KindValue {
+ invalid, /// for error condition
+
+ modeCode, /// Content starting at this offset is code.
+ modeData, /// Content starting at this offset is data.
+
+ // Kinds found in mach-o .o files:
+ branch32, /// ex: call _foo
+ branch16, /// ex: callw _foo
+ abs32, /// ex: movl _foo, %eax
+ funcRel32, /// ex: movl _foo-L1(%eax), %eax
+ pointer32, /// ex: .long _foo
+ delta32, /// ex: .long _foo - .
+ negDelta32, /// ex: .long . - _foo
+
+ // Kinds introduced by Passes:
+ lazyPointer, /// Location contains a lazy pointer.
+ lazyImmediateLocation, /// Location contains immediate value used in stub.
+ };
+
+ static bool useExternalRelocationTo(const Atom &target);
+
+ void applyFixupFinal(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress, uint64_t targetAddress,
+ uint64_t inAtomAddress);
+
+ void applyFixupRelocatable(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress);
+};
+
+//===----------------------------------------------------------------------===//
+// ArchHandler_x86
+//===----------------------------------------------------------------------===//
+
+const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
+ LLD_KIND_STRING_ENTRY(invalid),
+ LLD_KIND_STRING_ENTRY(modeCode),
+ LLD_KIND_STRING_ENTRY(modeData),
+ LLD_KIND_STRING_ENTRY(branch32),
+ LLD_KIND_STRING_ENTRY(branch16),
+ LLD_KIND_STRING_ENTRY(abs32),
+ LLD_KIND_STRING_ENTRY(funcRel32),
+ LLD_KIND_STRING_ENTRY(pointer32),
+ LLD_KIND_STRING_ENTRY(delta32),
+ LLD_KIND_STRING_ENTRY(negDelta32),
+ LLD_KIND_STRING_ENTRY(lazyPointer),
+ LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
+ LLD_KIND_STRING_END
+};
+
+const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
+ "dyld_stub_binder",
+
+ // Lazy pointer references
+ { Reference::KindArch::x86, pointer32, 0, 0 },
+ { Reference::KindArch::x86, lazyPointer, 0, 0 },
+
+ // GOT pointer to dyld_stub_binder
+ { Reference::KindArch::x86, pointer32, 0, 0 },
+
+ // x86 code alignment
+ 1,
+
+ // Stub size and code
+ 6,
+ { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
+ { Reference::KindArch::x86, abs32, 2, 0 },
+ { false, 0, 0, 0 },
+
+ // Stub Helper size and code
+ 10,
+ { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset
+ 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
+ { Reference::KindArch::x86, lazyImmediateLocation, 1, 0 },
+ { Reference::KindArch::x86, branch32, 6, 0 },
+
+ // Stub helper image cache content type
+ DefinedAtom::typeNonLazyPointer,
+
+ // Stub Helper-Common size and code
+ 12,
+ // Stub helper alignment
+ 2,
+ { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
+ 0x90 }, // nop
+ { Reference::KindArch::x86, abs32, 1, 0 },
+ { false, 0, 0, 0 },
+ { Reference::KindArch::x86, abs32, 7, 0 },
+ { false, 0, 0, 0 }
+};
+
+bool ArchHandler_x86::isCallSite(const Reference &ref) {
+ return (ref.kindValue() == branch32);
+}
+
+bool ArchHandler_x86::isPointer(const Reference &ref) {
+ return (ref.kindValue() == pointer32);
+}
+
+bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) {
+ if (!reloc.scattered)
+ return false;
+ return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) ||
+ (reloc.type == GENERIC_RELOC_SECTDIFF);
+}
+
+llvm::Error
+ArchHandler_x86::getReferenceInfo(const Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) {
+ DefinedAtom::ContentPermissions perms;
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ uint64_t targetAddress;
+ switch (relocPattern(reloc)) {
+ case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4:
+ // ex: call _foo (and _foo undefined)
+ *kind = branch32;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent;
+ break;
+ case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
+ // ex: call _foo (and _foo defined)
+ *kind = branch32;
+ targetAddress =
+ fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ break;
+ case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4:
+ // ex: call _foo+n (and _foo defined)
+ *kind = branch32;
+ targetAddress =
+ fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ *addend = targetAddress - reloc.value;
+ break;
+ case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
+ // ex: callw _foo (and _foo undefined)
+ *kind = branch16;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent;
+ break;
+ case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
+ // ex: callw _foo (and _foo defined)
+ *kind = branch16;
+ targetAddress =
+ fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ break;
+ case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2:
+ // ex: callw _foo+n (and _foo defined)
+ *kind = branch16;
+ targetAddress =
+ fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ *addend = targetAddress - reloc.value;
+ break;
+ case GENERIC_RELOC_VANILLA | rExtern | rLength4:
+ // ex: movl _foo, %eax (and _foo undefined)
+ // ex: .long _foo (and _foo undefined)
+ perms = inAtom->permissions();
+ *kind =
+ ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
+ : pointer32;
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = *(const ulittle32_t *)fixupContent;
+ break;
+ case GENERIC_RELOC_VANILLA | rLength4:
+ // ex: movl _foo, %eax (and _foo defined)
+ // ex: .long _foo (and _foo defined)
+ perms = inAtom->permissions();
+ *kind =
+ ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
+ : pointer32;
+ targetAddress = *(const ulittle32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ break;
+ case GENERIC_RELOC_VANILLA | rScattered | rLength4:
+ // ex: .long _foo+n (and _foo defined)
+ perms = inAtom->permissions();
+ *kind =
+ ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
+ : pointer32;
+ if (auto ec = atomFromAddress(0, reloc.value, target, addend))
+ return ec;
+ *addend = *(const ulittle32_t *)fixupContent - reloc.value;
+ break;
+ default:
+ return llvm::make_error<GenericError>("unsupported i386 relocation type");
+ }
+ return llvm::Error::success();
+}
+
+llvm::Error
+ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddr,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) {
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ DefinedAtom::ContentPermissions perms = inAtom->permissions();
+ uint32_t fromAddress;
+ uint32_t toAddress;
+ uint32_t value;
+ const lld::Atom *fromTarget;
+ Reference::Addend offsetInTo;
+ Reference::Addend offsetInFrom;
+ switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
+ case ((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
+ GENERIC_RELOC_PAIR | rScattered | rLength4):
+ case ((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
+ GENERIC_RELOC_PAIR | rScattered | rLength4):
+ toAddress = reloc1.value;
+ fromAddress = reloc2.value;
+ value = *(const little32_t *)fixupContent;
+ if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo))
+ return ec;
+ if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom))
+ return ec;
+ if (fromTarget != inAtom) {
+ if (*target != inAtom)
+ return llvm::make_error<GenericError>(
+ "SECTDIFF relocation where neither target is in atom");
+ *kind = negDelta32;
+ *addend = toAddress - value - fromAddress;
+ *target = fromTarget;
+ } else {
+ if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) {
+ // SECTDIFF relocations are used in i386 codegen where the function
+ // prolog does a CALL to the next instruction which POPs the return
+ // address into EBX which becomes the pic-base register. The POP
+ // instruction is label the used for the subtrahend in expressions.
+ // The funcRel32 kind represents the 32-bit delta to some symbol from
+ // the start of the function (atom) containing the funcRel32.
+ *kind = funcRel32;
+ uint32_t ta = fromAddress + value - toAddress;
+ *addend = ta - offsetInFrom;
+ } else {
+ *kind = delta32;
+ *addend = fromAddress + value - toAddress;
+ }
+ }
+ return llvm::Error::success();
+ break;
+ default:
+ return llvm::make_error<GenericError>("unsupported i386 relocation type");
+ }
+}
+
+void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom,
+ bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
+ // Copy raw bytes.
+ std::copy(atom.rawContent().begin(), atom.rawContent().end(),
+ atomContentBuffer.begin());
+ // Apply fix-ups.
+ for (const Reference *ref : atom) {
+ uint32_t offset = ref->offsetInAtom();
+ const Atom *target = ref->target();
+ uint64_t targetAddress = 0;
+ if (isa<DefinedAtom>(target))
+ targetAddress = findAddress(*target);
+ uint64_t atomAddress = findAddress(atom);
+ uint64_t fixupAddress = atomAddress + offset;
+ if (relocatable) {
+ applyFixupRelocatable(*ref, &atomContentBuffer[offset],
+ fixupAddress, targetAddress,
+ atomAddress);
+ } else {
+ applyFixupFinal(*ref, &atomContentBuffer[offset],
+ fixupAddress, targetAddress,
+ atomAddress);
+ }
+ }
+}
+
+void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ switch (static_cast<X86Kind>(ref.kindValue())) {
+ case branch32:
+ *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
+ break;
+ case branch16:
+ *loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend();
+ break;
+ case pointer32:
+ case abs32:
+ *loc32 = targetAddress + ref.addend();
+ break;
+ case funcRel32:
+ *loc32 = targetAddress - inAtomAddress + ref.addend();
+ break;
+ case delta32:
+ *loc32 = targetAddress - fixupAddress + ref.addend();
+ break;
+ case negDelta32:
+ *loc32 = fixupAddress - targetAddress + ref.addend();
+ break;
+ case modeCode:
+ case modeData:
+ case lazyPointer:
+ // do nothing
+ break;
+ case lazyImmediateLocation:
+ *loc32 = ref.addend();
+ break;
+ case invalid:
+ llvm_unreachable("invalid x86 Reference Kind");
+ break;
+ }
+}
+
+void ArchHandler_x86::applyFixupRelocatable(const Reference &ref,
+ uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86);
+ bool useExternalReloc = useExternalRelocationTo(*ref.target());
+ ulittle16_t *loc16 = reinterpret_cast<ulittle16_t *>(loc);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ switch (static_cast<X86Kind>(ref.kindValue())) {
+ case branch32:
+ if (useExternalReloc)
+ *loc32 = ref.addend() - (fixupAddress + 4);
+ else
+ *loc32 =(targetAddress - (fixupAddress+4)) + ref.addend();
+ break;
+ case branch16:
+ if (useExternalReloc)
+ *loc16 = ref.addend() - (fixupAddress + 2);
+ else
+ *loc16 = (targetAddress - (fixupAddress+2)) + ref.addend();
+ break;
+ case pointer32:
+ case abs32:
+ *loc32 = targetAddress + ref.addend();
+ break;
+ case funcRel32:
+ *loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME
+ break;
+ case delta32:
+ *loc32 = targetAddress - fixupAddress + ref.addend();
+ break;
+ case negDelta32:
+ *loc32 = fixupAddress - targetAddress + ref.addend();
+ break;
+ case modeCode:
+ case modeData:
+ case lazyPointer:
+ case lazyImmediateLocation:
+ // do nothing
+ break;
+ case invalid:
+ llvm_unreachable("invalid x86 Reference Kind");
+ break;
+ }
+}
+
+bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) {
+ // Undefined symbols are referenced via external relocations.
+ if (isa<UndefinedAtom>(&target))
+ return true;
+ if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) {
+ switch (defAtom->merge()) {
+ case DefinedAtom::mergeAsTentative:
+ // Tentative definitions are referenced via external relocations.
+ return true;
+ case DefinedAtom::mergeAsWeak:
+ case DefinedAtom::mergeAsWeakAndAddressUsed:
+ // Global weak-defs are referenced via external relocations.
+ return (defAtom->scope() == DefinedAtom::scopeGlobal);
+ default:
+ break;
+ }
+ }
+ // Everything else is reference via an internal relocation.
+ return false;
+}
+
+void ArchHandler_x86::appendSectionRelocations(
+ const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86);
+ uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
+ bool useExternalReloc = useExternalRelocationTo(*ref.target());
+ switch (static_cast<X86Kind>(ref.kindValue())) {
+ case modeCode:
+ case modeData:
+ break;
+ case branch32:
+ if (useExternalReloc) {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4);
+ } else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ GENERIC_RELOC_VANILLA | rPcRel | rLength4);
+ }
+ break;
+ case branch16:
+ if (useExternalReloc) {
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2);
+ } else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0,
+ GENERIC_RELOC_VANILLA | rPcRel | rLength2);
+ }
+ break;
+ case pointer32:
+ case abs32:
+ if (useExternalReloc)
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ GENERIC_RELOC_VANILLA | rExtern | rLength4);
+ else {
+ if (ref.addend() != 0)
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_VANILLA | rScattered | rLength4);
+ else
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ GENERIC_RELOC_VANILLA | rLength4);
+ }
+ break;
+ case funcRel32:
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(),
+ GENERIC_RELOC_PAIR | rScattered | rLength4);
+ break;
+ case delta32:
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
+ ref.offsetInAtom(),
+ GENERIC_RELOC_PAIR | rScattered | rLength4);
+ break;
+ case negDelta32:
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) +
+ ref.offsetInAtom(),
+ GENERIC_RELOC_SECTDIFF | rScattered | rLength4);
+ appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()),
+ GENERIC_RELOC_PAIR | rScattered | rLength4);
+ break;
+ case lazyPointer:
+ case lazyImmediateLocation:
+ llvm_unreachable("lazy reference kind implies Stubs pass was run");
+ break;
+ case invalid:
+ llvm_unreachable("unknown x86 Reference Kind");
+ break;
+ }
+}
+
+std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() {
+ return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86());
+}
+
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+namespace lld {
+namespace mach_o {
+
+using llvm::support::ulittle32_t;
+using llvm::support::ulittle64_t;
+
+using llvm::support::little32_t;
+using llvm::support::little64_t;
+
+class ArchHandler_x86_64 : public ArchHandler {
+public:
+ ArchHandler_x86_64() = default;
+ ~ArchHandler_x86_64() override = default;
+
+ const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
+
+ Reference::KindArch kindArch() override {
+ return Reference::KindArch::x86_64;
+ }
+
+ /// Used by GOTPass to locate GOT References
+ bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ switch (ref.kindValue()) {
+ case ripRel32GotLoad:
+ canBypassGOT = true;
+ return true;
+ case ripRel32Got:
+ canBypassGOT = false;
+ return true;
+ case imageOffsetGot:
+ canBypassGOT = false;
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isTLVAccess(const Reference &ref) const override {
+ assert(ref.kindNamespace() == Reference::KindNamespace::mach_o);
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ return ref.kindValue() == ripRel32Tlv;
+ }
+
+ void updateReferenceToTLV(const Reference *ref) override {
+ assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
+ assert(ref->kindArch() == Reference::KindArch::x86_64);
+ assert(ref->kindValue() == ripRel32Tlv);
+ const_cast<Reference*>(ref)->setKindValue(ripRel32);
+ }
+
+ /// Used by GOTPass to update GOT References
+ void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
+ assert(ref->kindNamespace() == Reference::KindNamespace::mach_o);
+ assert(ref->kindArch() == Reference::KindArch::x86_64);
+
+ switch (ref->kindValue()) {
+ case ripRel32Got:
+ assert(targetNowGOT && "target must be GOT");
+ LLVM_FALLTHROUGH;
+ case ripRel32GotLoad:
+ const_cast<Reference *>(ref)
+ ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea);
+ break;
+ case imageOffsetGot:
+ const_cast<Reference *>(ref)->setKindValue(imageOffset);
+ break;
+ default:
+ llvm_unreachable("unknown GOT reference kind");
+ }
+ }
+
+ bool needsCompactUnwind() override {
+ return true;
+ }
+
+ Reference::KindValue imageOffsetKind() override {
+ return imageOffset;
+ }
+
+ Reference::KindValue imageOffsetKindIndirect() override {
+ return imageOffsetGot;
+ }
+
+ Reference::KindValue unwindRefToPersonalityFunctionKind() override {
+ return ripRel32Got;
+ }
+
+ Reference::KindValue unwindRefToCIEKind() override {
+ return negDelta32;
+ }
+
+ Reference::KindValue unwindRefToFunctionKind() override{
+ return unwindFDEToFunction;
+ }
+
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
+ Reference::KindValue unwindRefToEhFrameKind() override {
+ return unwindInfoToEhFrame;
+ }
+
+ Reference::KindValue pointerKind() override {
+ return pointer64;
+ }
+
+ uint32_t dwarfCompactUnwindType() override {
+ return 0x04000000U;
+ }
+
+ const StubInfo &stubInfo() override { return _sStubInfo; }
+
+ bool isNonCallBranch(const Reference &) override {
+ return false;
+ }
+
+ bool isCallSite(const Reference &) override;
+ bool isPointer(const Reference &) override;
+ bool isPairedReloc(const normalized::Relocation &) override;
+
+ llvm::Error getReferenceInfo(const normalized::Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+ llvm::Error
+ getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap, bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) override;
+
+ bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override {
+ return (atom->contentType() == DefinedAtom::typeCString);
+ }
+
+ void generateAtomContent(const DefinedAtom &atom, bool relocatable,
+ FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress,
+ uint64_t imageBase,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) override;
+
+ void appendSectionRelocations(const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) override;
+
+ bool isDataInCodeTransition(Reference::KindValue refKind) override {
+ return refKind == modeCode || refKind == modeData;
+ }
+
+ Reference::KindValue dataInCodeTransitionStart(
+ const MachODefinedAtom &atom) override {
+ return modeData;
+ }
+
+ Reference::KindValue dataInCodeTransitionEnd(
+ const MachODefinedAtom &atom) override {
+ return modeCode;
+ }
+
+private:
+ static const Registry::KindStrings _sKindStrings[];
+ static const StubInfo _sStubInfo;
+
+ enum X86_64Kind: Reference::KindValue {
+ invalid, /// for error condition
+
+ modeCode, /// Content starting at this offset is code.
+ modeData, /// Content starting at this offset is data.
+
+ // Kinds found in mach-o .o files:
+ branch32, /// ex: call _foo
+ ripRel32, /// ex: movq _foo(%rip), %rax
+ ripRel32Minus1, /// ex: movb $0x12, _foo(%rip)
+ ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip)
+ ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip)
+ ripRel32Anon, /// ex: movq L1(%rip), %rax
+ ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip)
+ ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip)
+ ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip)
+ ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax
+ ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip)
+ ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi
+ pointer64, /// ex: .quad _foo
+ pointer64Anon, /// ex: .quad L1
+ delta64, /// ex: .quad _foo - .
+ delta32, /// ex: .long _foo - .
+ delta64Anon, /// ex: .quad L1 - .
+ delta32Anon, /// ex: .long L1 - .
+ negDelta64, /// ex: .quad . - _foo
+ negDelta32, /// ex: .long . - _foo
+
+ // Kinds introduced by Passes:
+ ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so
+ /// "movq _foo@GOTPCREL(%rip), %rax" can be changed
+ /// to "leaq _foo(%rip), %rax
+ lazyPointer, /// Location contains a lazy pointer.
+ lazyImmediateLocation, /// Location contains immediate value used in stub.
+
+ imageOffset, /// Location contains offset of atom in final image
+ imageOffsetGot, /// Location contains offset of GOT entry for atom in
+ /// final image (typically personality function).
+ unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in
+ /// relocatable object (yay for implicit contracts!).
+ unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to
+ /// refer to __eh_frame entry.
+ tlvInitSectionOffset /// Location contains offset tlv init-value atom
+ /// within the __thread_data section.
+ };
+
+ Reference::KindValue kindFromReloc(const normalized::Relocation &reloc);
+
+ void applyFixupFinal(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress, uint64_t targetAddress,
+ uint64_t inAtomAddress, uint64_t imageBaseAddress,
+ FindAddressForAtom findSectionAddress);
+
+ void applyFixupRelocatable(const Reference &ref, uint8_t *location,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress);
+};
+
+const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
+ LLD_KIND_STRING_ENTRY(invalid),
+ LLD_KIND_STRING_ENTRY(modeCode),
+ LLD_KIND_STRING_ENTRY(modeData),
+ LLD_KIND_STRING_ENTRY(branch32),
+ LLD_KIND_STRING_ENTRY(ripRel32),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus1),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus2),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus4),
+ LLD_KIND_STRING_ENTRY(ripRel32Anon),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
+ LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
+ LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
+ LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
+ LLD_KIND_STRING_ENTRY(ripRel32Got),
+ LLD_KIND_STRING_ENTRY(ripRel32Tlv),
+ LLD_KIND_STRING_ENTRY(lazyPointer),
+ LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
+ LLD_KIND_STRING_ENTRY(pointer64),
+ LLD_KIND_STRING_ENTRY(pointer64Anon),
+ LLD_KIND_STRING_ENTRY(delta32),
+ LLD_KIND_STRING_ENTRY(delta64),
+ LLD_KIND_STRING_ENTRY(delta32Anon),
+ LLD_KIND_STRING_ENTRY(delta64Anon),
+ LLD_KIND_STRING_ENTRY(negDelta64),
+ LLD_KIND_STRING_ENTRY(negDelta32),
+ LLD_KIND_STRING_ENTRY(imageOffset),
+ LLD_KIND_STRING_ENTRY(imageOffsetGot),
+ LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
+ LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
+ LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
+ LLD_KIND_STRING_END
+};
+
+const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = {
+ "dyld_stub_binder",
+
+ // Lazy pointer references
+ { Reference::KindArch::x86_64, pointer64, 0, 0 },
+ { Reference::KindArch::x86_64, lazyPointer, 0, 0 },
+
+ // GOT pointer to dyld_stub_binder
+ { Reference::KindArch::x86_64, pointer64, 0, 0 },
+
+ // x86_64 code alignment 2^1
+ 1,
+
+ // Stub size and code
+ 6,
+ { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
+ { Reference::KindArch::x86_64, ripRel32, 2, 0 },
+ { false, 0, 0, 0 },
+
+ // Stub Helper size and code
+ 10,
+ { 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset
+ 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
+ { Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 },
+ { Reference::KindArch::x86_64, branch32, 6, 0 },
+
+ // Stub helper image cache content type
+ DefinedAtom::typeNonLazyPointer,
+
+ // Stub Helper-Common size and code
+ 16,
+ // Stub helper alignment
+ 2,
+ { 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11
+ 0x41, 0x53, // push %r11
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip)
+ 0x90 }, // nop
+ { Reference::KindArch::x86_64, ripRel32, 3, 0 },
+ { false, 0, 0, 0 },
+ { Reference::KindArch::x86_64, ripRel32, 11, 0 },
+ { false, 0, 0, 0 }
+
+};
+
+bool ArchHandler_x86_64::isCallSite(const Reference &ref) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ return (ref.kindValue() == branch32);
+}
+
+bool ArchHandler_x86_64::isPointer(const Reference &ref) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return false;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ Reference::KindValue kind = ref.kindValue();
+ return (kind == pointer64 || kind == pointer64Anon);
+}
+
+bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) {
+ return (reloc.type == X86_64_RELOC_SUBTRACTOR);
+}
+
+Reference::KindValue
+ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) {
+ switch(relocPattern(reloc)) {
+ case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4:
+ return branch32;
+ case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4:
+ return ripRel32;
+ case X86_64_RELOC_SIGNED | rPcRel | rLength4:
+ return ripRel32Anon;
+ case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4:
+ return ripRel32Minus1;
+ case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4:
+ return ripRel32Minus1Anon;
+ case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4:
+ return ripRel32Minus2;
+ case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4:
+ return ripRel32Minus2Anon;
+ case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4:
+ return ripRel32Minus4;
+ case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4:
+ return ripRel32Minus4Anon;
+ case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4:
+ return ripRel32GotLoad;
+ case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4:
+ return ripRel32Got;
+ case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4:
+ return ripRel32Tlv;
+ case X86_64_RELOC_UNSIGNED | rExtern | rLength8:
+ return pointer64;
+ case X86_64_RELOC_UNSIGNED | rLength8:
+ return pointer64Anon;
+ default:
+ return invalid;
+ }
+}
+
+llvm::Error
+ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) {
+ *kind = kindFromReloc(reloc);
+ if (*kind == invalid)
+ return llvm::make_error<GenericError>("unknown type");
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ uint64_t targetAddress;
+ switch (*kind) {
+ case branch32:
+ case ripRel32:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = *(const little32_t *)fixupContent;
+ return llvm::Error::success();
+ case ripRel32Minus1:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = (int32_t)*(const little32_t *)fixupContent + 1;
+ return llvm::Error::success();
+ case ripRel32Minus2:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = (int32_t)*(const little32_t *)fixupContent + 2;
+ return llvm::Error::success();
+ case ripRel32Minus4:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = (int32_t)*(const little32_t *)fixupContent + 4;
+ return llvm::Error::success();
+ case ripRel32Anon:
+ targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ripRel32Minus1Anon:
+ targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ripRel32Minus2Anon:
+ targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ripRel32Minus4Anon:
+ targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ case ripRel32GotLoad:
+ case ripRel32Got:
+ case ripRel32Tlv:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ *addend = *(const little32_t *)fixupContent;
+ return llvm::Error::success();
+ case tlvInitSectionOffset:
+ case pointer64:
+ if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
+ return ec;
+ // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's
+ // initial value) we need to handle it specially.
+ if (inAtom->contentType() == DefinedAtom::typeThunkTLV &&
+ offsetInAtom == 16) {
+ *kind = tlvInitSectionOffset;
+ assert(*addend == 0 && "TLV-init has non-zero addend?");
+ } else
+ *addend = *(const little64_t *)fixupContent;
+ return llvm::Error::success();
+ case pointer64Anon:
+ targetAddress = *(const little64_t *)fixupContent;
+ return atomFromAddress(reloc.symbol, targetAddress, target, addend);
+ default:
+ llvm_unreachable("bad reloc kind");
+ }
+}
+
+llvm::Error
+ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
+ const normalized::Relocation &reloc2,
+ const DefinedAtom *inAtom,
+ uint32_t offsetInAtom,
+ uint64_t fixupAddress, bool swap,
+ bool scatterable,
+ FindAtomBySectionAndAddress atomFromAddress,
+ FindAtomBySymbolIndex atomFromSymbolIndex,
+ Reference::KindValue *kind,
+ const lld::Atom **target,
+ Reference::Addend *addend) {
+ const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
+ uint64_t targetAddress;
+ const lld::Atom *fromTarget;
+ if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget))
+ return ec;
+
+ switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
+ case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
+ X86_64_RELOC_UNSIGNED | rExtern | rLength8): {
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent;
+ if (inAtom == fromTarget) {
+ if (inAtom->contentType() == DefinedAtom::typeCFI)
+ *kind = unwindFDEToFunction;
+ else
+ *kind = delta64;
+ *addend = encodedAddend + offsetInAtom;
+ } else if (inAtom == *target) {
+ *kind = negDelta64;
+ *addend = encodedAddend - offsetInAtom;
+ *target = fromTarget;
+ } else
+ return llvm::make_error<GenericError>("Invalid pointer diff");
+ return llvm::Error::success();
+ }
+ case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
+ X86_64_RELOC_UNSIGNED | rExtern | rLength4): {
+ if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
+ return ec;
+ uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent;
+ if (inAtom == fromTarget) {
+ *kind = delta32;
+ *addend = encodedAddend + offsetInAtom;
+ } else if (inAtom == *target) {
+ *kind = negDelta32;
+ *addend = encodedAddend - offsetInAtom;
+ *target = fromTarget;
+ } else
+ return llvm::make_error<GenericError>("Invalid pointer diff");
+ return llvm::Error::success();
+ }
+ case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
+ X86_64_RELOC_UNSIGNED | rLength8):
+ if (fromTarget != inAtom)
+ return llvm::make_error<GenericError>("pointer diff not in base atom");
+ *kind = delta64Anon;
+ targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent;
+ return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
+ case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
+ X86_64_RELOC_UNSIGNED | rLength4):
+ if (fromTarget != inAtom)
+ return llvm::make_error<GenericError>("pointer diff not in base atom");
+ *kind = delta32Anon;
+ targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent;
+ return atomFromAddress(reloc2.symbol, targetAddress, target, addend);
+ default:
+ return llvm::make_error<GenericError>("unknown pair");
+ }
+}
+
+void ArchHandler_x86_64::generateAtomContent(
+ const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress,
+ FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress,
+ llvm::MutableArrayRef<uint8_t> atomContentBuffer) {
+ // Copy raw bytes.
+ std::copy(atom.rawContent().begin(), atom.rawContent().end(),
+ atomContentBuffer.begin());
+ // Apply fix-ups.
+ for (const Reference *ref : atom) {
+ uint32_t offset = ref->offsetInAtom();
+ const Atom *target = ref->target();
+ uint64_t targetAddress = 0;
+ if (isa<DefinedAtom>(target))
+ targetAddress = findAddress(*target);
+ uint64_t atomAddress = findAddress(atom);
+ uint64_t fixupAddress = atomAddress + offset;
+ if (relocatable) {
+ applyFixupRelocatable(*ref, &atomContentBuffer[offset],
+ fixupAddress, targetAddress,
+ atomAddress);
+ } else {
+ applyFixupFinal(*ref, &atomContentBuffer[offset],
+ fixupAddress, targetAddress,
+ atomAddress, imageBaseAddress, findSectionAddress);
+ }
+ }
+}
+
+void ArchHandler_x86_64::applyFixupFinal(
+ const Reference &ref, uint8_t *loc, uint64_t fixupAddress,
+ uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress,
+ FindAddressForAtom findSectionAddress) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
+ switch (static_cast<X86_64Kind>(ref.kindValue())) {
+ case branch32:
+ case ripRel32:
+ case ripRel32Anon:
+ case ripRel32Got:
+ case ripRel32GotLoad:
+ case ripRel32Tlv:
+ *loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
+ return;
+ case pointer64:
+ case pointer64Anon:
+ *loc64 = targetAddress + ref.addend();
+ return;
+ case tlvInitSectionOffset:
+ *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
+ return;
+ case ripRel32Minus1:
+ case ripRel32Minus1Anon:
+ *loc32 = targetAddress - (fixupAddress + 5) + ref.addend();
+ return;
+ case ripRel32Minus2:
+ case ripRel32Minus2Anon:
+ *loc32 = targetAddress - (fixupAddress + 6) + ref.addend();
+ return;
+ case ripRel32Minus4:
+ case ripRel32Minus4Anon:
+ *loc32 = targetAddress - (fixupAddress + 8) + ref.addend();
+ return;
+ case delta32:
+ case delta32Anon:
+ *loc32 = targetAddress - fixupAddress + ref.addend();
+ return;
+ case delta64:
+ case delta64Anon:
+ case unwindFDEToFunction:
+ *loc64 = targetAddress - fixupAddress + ref.addend();
+ return;
+ case ripRel32GotLoadNowLea:
+ // Change MOVQ to LEA
+ assert(loc[-2] == 0x8B);
+ loc[-2] = 0x8D;
+ *loc32 = targetAddress - (fixupAddress + 4) + ref.addend();
+ return;
+ case negDelta64:
+ *loc64 = fixupAddress - targetAddress + ref.addend();
+ return;
+ case negDelta32:
+ *loc32 = fixupAddress - targetAddress + ref.addend();
+ return;
+ case modeCode:
+ case modeData:
+ case lazyPointer:
+ // Do nothing
+ return;
+ case lazyImmediateLocation:
+ *loc32 = ref.addend();
+ return;
+ case imageOffset:
+ case imageOffsetGot:
+ *loc32 = (targetAddress - imageBaseAddress) + ref.addend();
+ return;
+ case unwindInfoToEhFrame: {
+ uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend();
+ assert(val < 0xffffffU && "offset in __eh_frame too large");
+ *loc32 = (*loc32 & 0xff000000U) | val;
+ return;
+ }
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("invalid x86_64 Reference Kind");
+}
+
+void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
+ uint8_t *loc,
+ uint64_t fixupAddress,
+ uint64_t targetAddress,
+ uint64_t inAtomAddress) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc);
+ ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc);
+ switch (static_cast<X86_64Kind>(ref.kindValue())) {
+ case branch32:
+ case ripRel32:
+ case ripRel32Got:
+ case ripRel32GotLoad:
+ case ripRel32Tlv:
+ *loc32 = ref.addend();
+ return;
+ case ripRel32Anon:
+ *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend();
+ return;
+ case tlvInitSectionOffset:
+ case pointer64:
+ *loc64 = ref.addend();
+ return;
+ case pointer64Anon:
+ *loc64 = targetAddress + ref.addend();
+ return;
+ case ripRel32Minus1:
+ *loc32 = ref.addend() - 1;
+ return;
+ case ripRel32Minus1Anon:
+ *loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend();
+ return;
+ case ripRel32Minus2:
+ *loc32 = ref.addend() - 2;
+ return;
+ case ripRel32Minus2Anon:
+ *loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend();
+ return;
+ case ripRel32Minus4:
+ *loc32 = ref.addend() - 4;
+ return;
+ case ripRel32Minus4Anon:
+ *loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend();
+ return;
+ case delta32:
+ *loc32 = ref.addend() + inAtomAddress - fixupAddress;
+ return;
+ case delta32Anon:
+ // The value we write here should be the delta to the target
+ // after taking in to account the difference from the fixup back to the
+ // last defined label
+ // ie, if we have:
+ // _base: ...
+ // Lfixup: .quad Ltarget - .
+ // ...
+ // Ltarget:
+ //
+ // Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
+ *loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
+ return;
+ case delta64:
+ *loc64 = ref.addend() + inAtomAddress - fixupAddress;
+ return;
+ case delta64Anon:
+ // The value we write here should be the delta to the target
+ // after taking in to account the difference from the fixup back to the
+ // last defined label
+ // ie, if we have:
+ // _base: ...
+ // Lfixup: .quad Ltarget - .
+ // ...
+ // Ltarget:
+ //
+ // Then we want to encode the value (Ltarget + addend) - (LFixup - _base)
+ *loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress);
+ return;
+ case negDelta64:
+ *loc64 = ref.addend() + fixupAddress - inAtomAddress;
+ return;
+ case negDelta32:
+ *loc32 = ref.addend() + fixupAddress - inAtomAddress;
+ return;
+ case ripRel32GotLoadNowLea:
+ llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
+ return;
+ case lazyPointer:
+ case lazyImmediateLocation:
+ llvm_unreachable("lazy reference kind implies Stubs pass was run");
+ return;
+ case imageOffset:
+ case imageOffsetGot:
+ case unwindInfoToEhFrame:
+ llvm_unreachable("fixup implies __unwind_info");
+ return;
+ case modeCode:
+ case modeData:
+ case unwindFDEToFunction:
+ // Do nothing for now
+ return;
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("unknown x86_64 Reference Kind");
+}
+
+void ArchHandler_x86_64::appendSectionRelocations(
+ const DefinedAtom &atom,
+ uint64_t atomSectionOffset,
+ const Reference &ref,
+ FindSymbolIndexForAtom symbolIndexForAtom,
+ FindSectionIndexForAtom sectionIndexForAtom,
+ FindAddressForAtom addressForAtom,
+ normalized::Relocations &relocs) {
+ if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
+ return;
+ assert(ref.kindArch() == Reference::KindArch::x86_64);
+ uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
+ switch (static_cast<X86_64Kind>(ref.kindValue())) {
+ case modeCode:
+ case modeData:
+ return;
+ case branch32:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
+ return;
+ case ripRel32:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32Anon:
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED | rPcRel | rLength4 );
+ return;
+ case ripRel32Got:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32GotLoad:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32Tlv:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 );
+ return;
+ case tlvInitSectionOffset:
+ case pointer64:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rExtern | rLength8);
+ return;
+ case pointer64Anon:
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rLength8);
+ return;
+ case ripRel32Minus1:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32Minus1Anon:
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 );
+ return;
+ case ripRel32Minus2:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32Minus2Anon:
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 );
+ return;
+ case ripRel32Minus4:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 );
+ return;
+ case ripRel32Minus4Anon:
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 );
+ return;
+ case delta32:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
+ return;
+ case delta32Anon:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rLength4 );
+ return;
+ case delta64:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
+ return;
+ case delta64Anon:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
+ appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_UNSIGNED | rLength8 );
+ return;
+ case unwindFDEToFunction:
+ case unwindInfoToEhFrame:
+ return;
+ case negDelta32:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 );
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_UNSIGNED | rExtern | rLength4 );
+ return;
+ case negDelta64:
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
+ X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 );
+ appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0,
+ X86_64_RELOC_UNSIGNED | rExtern | rLength8 );
+ return;
+ case ripRel32GotLoadNowLea:
+ llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run");
+ return;
+ case lazyPointer:
+ case lazyImmediateLocation:
+ llvm_unreachable("lazy reference kind implies Stubs pass was run");
+ return;
+ case imageOffset:
+ case imageOffsetGot:
+ llvm_unreachable("__unwind_info references should have been resolved");
+ return;
+ case invalid:
+ // Fall into llvm_unreachable().
+ break;
+ }
+ llvm_unreachable("unknown x86_64 Reference Kind");
+}
+
+std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() {
+ return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64());
+}
+
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/Atoms.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_ATOMS_H
+#define LLD_READER_WRITER_MACHO_ATOMS_H
+
+#include "lld/Core/Atom.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/SharedLibraryAtom.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
+#include <string>
+
+namespace lld {
+
+class File;
+
+namespace mach_o {
+
+class MachODefinedAtom : public SimpleDefinedAtom {
+public:
+ MachODefinedAtom(const File &f, const StringRef name, Scope scope,
+ ContentType type, Merge merge, bool thumb, bool noDeadStrip,
+ const ArrayRef<uint8_t> content, Alignment align)
+ : SimpleDefinedAtom(f), _name(name), _content(content),
+ _align(align), _contentType(type), _scope(scope), _merge(merge),
+ _thumb(thumb), _noDeadStrip(noDeadStrip) {}
+
+ // Constructor for zero-fill content
+ MachODefinedAtom(const File &f, const StringRef name, Scope scope,
+ ContentType type, uint64_t size, bool noDeadStrip,
+ Alignment align)
+ : SimpleDefinedAtom(f), _name(name),
+ _content(ArrayRef<uint8_t>(nullptr, size)), _align(align),
+ _contentType(type), _scope(scope), _merge(mergeNo), _thumb(false),
+ _noDeadStrip(noDeadStrip) {}
+
+ ~MachODefinedAtom() override = default;
+
+ uint64_t size() const override { return _content.size(); }
+
+ ContentType contentType() const override { return _contentType; }
+
+ Alignment alignment() const override { return _align; }
+
+ StringRef name() const override { return _name; }
+
+ Scope scope() const override { return _scope; }
+
+ Merge merge() const override { return _merge; }
+
+ DeadStripKind deadStrip() const override {
+ if (_contentType == DefinedAtom::typeInitializerPtr)
+ return deadStripNever;
+ if (_contentType == DefinedAtom::typeTerminatorPtr)
+ return deadStripNever;
+ if (_noDeadStrip)
+ return deadStripNever;
+ return deadStripNormal;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ // Note: Zerofill atoms have a content pointer which is null.
+ return _content;
+ }
+
+ bool isThumb() const { return _thumb; }
+
+private:
+ const StringRef _name;
+ const ArrayRef<uint8_t> _content;
+ const DefinedAtom::Alignment _align;
+ const ContentType _contentType;
+ const Scope _scope;
+ const Merge _merge;
+ const bool _thumb;
+ const bool _noDeadStrip;
+};
+
+class MachODefinedCustomSectionAtom : public MachODefinedAtom {
+public:
+ MachODefinedCustomSectionAtom(const File &f, const StringRef name,
+ Scope scope, ContentType type, Merge merge,
+ bool thumb, bool noDeadStrip,
+ const ArrayRef<uint8_t> content,
+ StringRef sectionName, Alignment align)
+ : MachODefinedAtom(f, name, scope, type, merge, thumb, noDeadStrip,
+ content, align),
+ _sectionName(sectionName) {}
+
+ ~MachODefinedCustomSectionAtom() override = default;
+
+ SectionChoice sectionChoice() const override {
+ return DefinedAtom::sectionCustomRequired;
+ }
+
+ StringRef customSectionName() const override {
+ return _sectionName;
+ }
+private:
+ StringRef _sectionName;
+};
+
+class MachOTentativeDefAtom : public SimpleDefinedAtom {
+public:
+ MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope,
+ uint64_t size, DefinedAtom::Alignment align)
+ : SimpleDefinedAtom(f), _name(std::string(name)), _scope(scope),
+ _size(size), _align(align) {}
+
+ ~MachOTentativeDefAtom() override = default;
+
+ uint64_t size() const override { return _size; }
+
+ Merge merge() const override { return DefinedAtom::mergeAsTentative; }
+
+ ContentType contentType() const override { return DefinedAtom::typeZeroFill; }
+
+ Alignment alignment() const override { return _align; }
+
+ StringRef name() const override { return _name; }
+
+ Scope scope() const override { return _scope; }
+
+ ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); }
+
+private:
+ const std::string _name;
+ const Scope _scope;
+ const uint64_t _size;
+ const DefinedAtom::Alignment _align;
+};
+
+class MachOSharedLibraryAtom : public SharedLibraryAtom {
+public:
+ MachOSharedLibraryAtom(const File &file, StringRef name,
+ StringRef dylibInstallName, bool weakDef)
+ : SharedLibraryAtom(), _file(file), _name(name),
+ _dylibInstallName(dylibInstallName) {}
+ ~MachOSharedLibraryAtom() override = default;
+
+ StringRef loadName() const override { return _dylibInstallName; }
+
+ bool canBeNullAtRuntime() const override {
+ // FIXME: this may actually be changeable. For now, all symbols are strongly
+ // defined though.
+ return false;
+ }
+
+ const File &file() const override { return _file; }
+
+ StringRef name() const override { return _name; }
+
+ Type type() const override {
+ // Unused in MachO (I think).
+ return Type::Unknown;
+ }
+
+ uint64_t size() const override {
+ // Unused in MachO (I think)
+ return 0;
+ }
+
+private:
+ const File &_file;
+ StringRef _name;
+ StringRef _dylibInstallName;
+};
+
+} // end namespace mach_o
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_ATOMS_H
--- /dev/null
+add_lld_library(lldMachOOld
+ ArchHandler.cpp
+ ArchHandler_arm.cpp
+ ArchHandler_arm64.cpp
+ ArchHandler_x86.cpp
+ ArchHandler_x86_64.cpp
+ CompactUnwindPass.cpp
+ GOTPass.cpp
+ LayoutPass.cpp
+ MachOLinkingContext.cpp
+ MachONormalizedFileBinaryReader.cpp
+ MachONormalizedFileBinaryWriter.cpp
+ MachONormalizedFileFromAtoms.cpp
+ MachONormalizedFileToAtoms.cpp
+ MachONormalizedFileYAML.cpp
+ ObjCPass.cpp
+ ShimPass.cpp
+ StubsPass.cpp
+ TLVPass.cpp
+ WriterMachO.cpp
+
+ LINK_COMPONENTS
+ DebugInfoDWARF
+ Demangle
+ Object
+ Support
+ TextAPI
+
+ LINK_LIBS
+ lldCommon
+ lldCore
+ lldYAML
+ ${LLVM_PTHREAD_LIB}
+ )
+
+include_directories(.)
--- /dev/null
+//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file A pass to convert MachO's __compact_unwind sections into the final
+/// __unwind_info format used during runtime. See
+/// mach-o/compact_unwind_encoding.h for more details on the formats involved.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+
+#define DEBUG_TYPE "macho-compact-unwind"
+
+namespace lld {
+namespace mach_o {
+
+namespace {
+struct CompactUnwindEntry {
+ const Atom *rangeStart;
+ const Atom *personalityFunction;
+ const Atom *lsdaLocation;
+ const Atom *ehFrame;
+
+ uint32_t rangeLength;
+
+ // There are 3 types of compact unwind entry, distinguished by the encoding
+ // value: 0 indicates a function with no unwind info;
+ // _archHandler.dwarfCompactUnwindType() indicates that the entry defers to
+ // __eh_frame, and that the ehFrame entry will be valid; any other value is a
+ // real compact unwind entry -- personalityFunction will be set and
+ // lsdaLocation may be.
+ uint32_t encoding;
+
+ CompactUnwindEntry(const DefinedAtom *function)
+ : rangeStart(function), personalityFunction(nullptr),
+ lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(function->size()),
+ encoding(0) {}
+
+ CompactUnwindEntry()
+ : rangeStart(nullptr), personalityFunction(nullptr),
+ lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(0), encoding(0) {}
+};
+
+struct UnwindInfoPage {
+ ArrayRef<CompactUnwindEntry> entries;
+};
+}
+
+class UnwindInfoAtom : public SimpleDefinedAtom {
+public:
+ UnwindInfoAtom(ArchHandler &archHandler, const File &file, bool isBig,
+ std::vector<const Atom *> &personalities,
+ std::vector<uint32_t> &commonEncodings,
+ std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs)
+ : SimpleDefinedAtom(file), _archHandler(archHandler),
+ _commonEncodingsOffset(7 * sizeof(uint32_t)),
+ _personalityArrayOffset(_commonEncodingsOffset +
+ commonEncodings.size() * sizeof(uint32_t)),
+ _topLevelIndexOffset(_personalityArrayOffset +
+ personalities.size() * sizeof(uint32_t)),
+ _lsdaIndexOffset(_topLevelIndexOffset +
+ 3 * (pages.size() + 1) * sizeof(uint32_t)),
+ _firstPageOffset(_lsdaIndexOffset + 2 * numLSDAs * sizeof(uint32_t)),
+ _isBig(isBig) {
+
+ addHeader(commonEncodings.size(), personalities.size(), pages.size());
+ addCommonEncodings(commonEncodings);
+ addPersonalityFunctions(personalities);
+ addTopLevelIndexes(pages);
+ addLSDAIndexes(pages, numLSDAs);
+ addSecondLevelPages(pages);
+ }
+
+ ~UnwindInfoAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeProcessedUnwindInfo;
+ }
+
+ Alignment alignment() const override { return 4; }
+
+ uint64_t size() const override { return _contents.size(); }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR__;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override { return _contents; }
+
+ void addHeader(uint32_t numCommon, uint32_t numPersonalities,
+ uint32_t numPages) {
+ using normalized::write32;
+
+ uint32_t headerSize = 7 * sizeof(uint32_t);
+ _contents.resize(headerSize);
+
+ uint8_t *headerEntries = _contents.data();
+ // version
+ write32(headerEntries, 1, _isBig);
+ // commonEncodingsArraySectionOffset
+ write32(headerEntries + sizeof(uint32_t), _commonEncodingsOffset, _isBig);
+ // commonEncodingsArrayCount
+ write32(headerEntries + 2 * sizeof(uint32_t), numCommon, _isBig);
+ // personalityArraySectionOffset
+ write32(headerEntries + 3 * sizeof(uint32_t), _personalityArrayOffset,
+ _isBig);
+ // personalityArrayCount
+ write32(headerEntries + 4 * sizeof(uint32_t), numPersonalities, _isBig);
+ // indexSectionOffset
+ write32(headerEntries + 5 * sizeof(uint32_t), _topLevelIndexOffset, _isBig);
+ // indexCount
+ write32(headerEntries + 6 * sizeof(uint32_t), numPages + 1, _isBig);
+ }
+
+ /// Add the list of common encodings to the section; this is simply an array
+ /// of uint32_t compact values. Size has already been specified in the header.
+ void addCommonEncodings(std::vector<uint32_t> &commonEncodings) {
+ using normalized::write32;
+
+ _contents.resize(_commonEncodingsOffset +
+ commonEncodings.size() * sizeof(uint32_t));
+ uint8_t *commonEncodingsArea =
+ reinterpret_cast<uint8_t *>(_contents.data() + _commonEncodingsOffset);
+
+ for (uint32_t encoding : commonEncodings) {
+ write32(commonEncodingsArea, encoding, _isBig);
+ commonEncodingsArea += sizeof(uint32_t);
+ }
+ }
+
+ void addPersonalityFunctions(std::vector<const Atom *> personalities) {
+ _contents.resize(_personalityArrayOffset +
+ personalities.size() * sizeof(uint32_t));
+
+ for (unsigned i = 0; i < personalities.size(); ++i)
+ addImageReferenceIndirect(_personalityArrayOffset + i * sizeof(uint32_t),
+ personalities[i]);
+ }
+
+ void addTopLevelIndexes(std::vector<UnwindInfoPage> &pages) {
+ using normalized::write32;
+
+ uint32_t numIndexes = pages.size() + 1;
+ _contents.resize(_topLevelIndexOffset + numIndexes * 3 * sizeof(uint32_t));
+
+ uint32_t pageLoc = _firstPageOffset;
+
+ // The most difficult job here is calculating the LSDAs; everything else
+ // follows fairly naturally, but we can't state where the first
+ uint8_t *indexData = &_contents[_topLevelIndexOffset];
+ uint32_t numLSDAs = 0;
+ for (unsigned i = 0; i < pages.size(); ++i) {
+ // functionOffset
+ addImageReference(_topLevelIndexOffset + 3 * i * sizeof(uint32_t),
+ pages[i].entries[0].rangeStart);
+ // secondLevelPagesSectionOffset
+ write32(indexData + (3 * i + 1) * sizeof(uint32_t), pageLoc, _isBig);
+ write32(indexData + (3 * i + 2) * sizeof(uint32_t),
+ _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
+
+ for (auto &entry : pages[i].entries)
+ if (entry.lsdaLocation)
+ ++numLSDAs;
+ }
+
+ // Finally, write out the final sentinel index
+ auto &finalEntry = pages[pages.size() - 1].entries.back();
+ addImageReference(_topLevelIndexOffset +
+ 3 * pages.size() * sizeof(uint32_t),
+ finalEntry.rangeStart, finalEntry.rangeLength);
+ // secondLevelPagesSectionOffset => 0
+ write32(indexData + (3 * pages.size() + 2) * sizeof(uint32_t),
+ _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig);
+ }
+
+ void addLSDAIndexes(std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) {
+ _contents.resize(_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t));
+
+ uint32_t curOffset = _lsdaIndexOffset;
+ for (auto &page : pages) {
+ for (auto &entry : page.entries) {
+ if (!entry.lsdaLocation)
+ continue;
+
+ addImageReference(curOffset, entry.rangeStart);
+ addImageReference(curOffset + sizeof(uint32_t), entry.lsdaLocation);
+ curOffset += 2 * sizeof(uint32_t);
+ }
+ }
+ }
+
+ void addSecondLevelPages(std::vector<UnwindInfoPage> &pages) {
+ for (auto &page : pages) {
+ addRegularSecondLevelPage(page);
+ }
+ }
+
+ void addRegularSecondLevelPage(const UnwindInfoPage &page) {
+ uint32_t curPageOffset = _contents.size();
+ const int16_t headerSize = sizeof(uint32_t) + 2 * sizeof(uint16_t);
+ uint32_t curPageSize =
+ headerSize + 2 * page.entries.size() * sizeof(uint32_t);
+ _contents.resize(curPageOffset + curPageSize);
+
+ using normalized::write32;
+ using normalized::write16;
+ // 2 => regular page
+ write32(&_contents[curPageOffset], 2, _isBig);
+ // offset of 1st entry
+ write16(&_contents[curPageOffset + 4], headerSize, _isBig);
+ write16(&_contents[curPageOffset + 6], page.entries.size(), _isBig);
+
+ uint32_t pagePos = curPageOffset + headerSize;
+ for (auto &entry : page.entries) {
+ addImageReference(pagePos, entry.rangeStart);
+
+ write32(_contents.data() + pagePos + sizeof(uint32_t), entry.encoding,
+ _isBig);
+ if ((entry.encoding & 0x0f000000U) ==
+ _archHandler.dwarfCompactUnwindType())
+ addEhFrameReference(pagePos + sizeof(uint32_t), entry.ehFrame);
+
+ pagePos += 2 * sizeof(uint32_t);
+ }
+ }
+
+ void addEhFrameReference(uint32_t offset, const Atom *dest,
+ Reference::Addend addend = 0) {
+ addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
+ _archHandler.unwindRefToEhFrameKind(), offset, dest, addend);
+ }
+
+ void addImageReference(uint32_t offset, const Atom *dest,
+ Reference::Addend addend = 0) {
+ addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
+ _archHandler.imageOffsetKind(), offset, dest, addend);
+ }
+
+ void addImageReferenceIndirect(uint32_t offset, const Atom *dest) {
+ addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(),
+ _archHandler.imageOffsetKindIndirect(), offset, dest, 0);
+ }
+
+private:
+ mach_o::ArchHandler &_archHandler;
+ std::vector<uint8_t> _contents;
+ uint32_t _commonEncodingsOffset;
+ uint32_t _personalityArrayOffset;
+ uint32_t _topLevelIndexOffset;
+ uint32_t _lsdaIndexOffset;
+ uint32_t _firstPageOffset;
+ bool _isBig;
+};
+
+/// Pass for instantiating and optimizing GOT slots.
+///
+class CompactUnwindPass : public Pass {
+public:
+ CompactUnwindPass(const MachOLinkingContext &context)
+ : _ctx(context), _archHandler(_ctx.archHandler()),
+ _file(*_ctx.make_file<MachOFile>("<mach-o Compact Unwind Pass>")),
+ _isBig(MachOLinkingContext::isBigEndian(_ctx.arch())) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+private:
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ LLVM_DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n");
+
+ std::map<const Atom *, CompactUnwindEntry> unwindLocs;
+ std::map<const Atom *, const Atom *> dwarfFrames;
+ std::vector<const Atom *> personalities;
+ uint32_t numLSDAs = 0;
+
+ // First collect all __compact_unwind and __eh_frame entries, addressable by
+ // the function referred to.
+ collectCompactUnwindEntries(mergedFile, unwindLocs, personalities,
+ numLSDAs);
+
+ collectDwarfFrameEntries(mergedFile, dwarfFrames);
+
+ // Skip rest of pass if no unwind info.
+ if (unwindLocs.empty() && dwarfFrames.empty())
+ return llvm::Error::success();
+
+ // FIXME: if there are more than 4 personality functions then we need to
+ // defer to DWARF info for the ones we don't put in the list. They should
+ // also probably be sorted by frequency.
+ assert(personalities.size() <= 4);
+
+ // TODO: Find common encodings for use by compressed pages.
+ std::vector<uint32_t> commonEncodings;
+
+ // Now sort the entries by final address and fixup the compact encoding to
+ // its final form (i.e. set personality function bits & create DWARF
+ // references where needed).
+ std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries(
+ mergedFile, unwindLocs, personalities, dwarfFrames);
+
+ // Remove any unused eh-frame atoms.
+ pruneUnusedEHFrames(mergedFile, unwindInfos, unwindLocs, dwarfFrames);
+
+ // Finally, we can start creating pages based on these entries.
+
+ LLVM_DEBUG(llvm::dbgs() << " Splitting entries into pages\n");
+ // FIXME: we split the entries into pages naively: lots of 4k pages followed
+ // by a small one. ld64 tried to minimize space and align them to real 4k
+ // boundaries. That might be worth doing, or perhaps we could perform some
+ // minor balancing for expected number of lookups.
+ std::vector<UnwindInfoPage> pages;
+ auto remainingInfos = llvm::makeArrayRef(unwindInfos);
+ do {
+ pages.push_back(UnwindInfoPage());
+
+ // FIXME: we only create regular pages at the moment. These can hold up to
+ // 1021 entries according to the documentation.
+ unsigned entriesInPage = std::min(1021U, (unsigned)remainingInfos.size());
+
+ pages.back().entries = remainingInfos.slice(0, entriesInPage);
+ remainingInfos = remainingInfos.slice(entriesInPage);
+
+ LLVM_DEBUG(llvm::dbgs()
+ << " Page from "
+ << pages.back().entries[0].rangeStart->name() << " to "
+ << pages.back().entries.back().rangeStart->name() << " + "
+ << llvm::format("0x%x",
+ pages.back().entries.back().rangeLength)
+ << " has " << entriesInPage << " entries\n");
+ } while (!remainingInfos.empty());
+
+ auto *unwind = new (_file.allocator())
+ UnwindInfoAtom(_archHandler, _file, _isBig, personalities,
+ commonEncodings, pages, numLSDAs);
+ mergedFile.addAtom(*unwind);
+
+ // Finally, remove all __compact_unwind atoms now that we've processed them.
+ mergedFile.removeDefinedAtomsIf([](const DefinedAtom *atom) {
+ return atom->contentType() == DefinedAtom::typeCompactUnwindInfo;
+ });
+
+ return llvm::Error::success();
+ }
+
+ void collectCompactUnwindEntries(
+ const SimpleFile &mergedFile,
+ std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
+ std::vector<const Atom *> &personalities, uint32_t &numLSDAs) {
+ LLVM_DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n");
+
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo)
+ continue;
+
+ auto unwindEntry = extractCompactUnwindEntry(atom);
+ unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry));
+
+ LLVM_DEBUG(llvm::dbgs() << " Entry for "
+ << unwindEntry.rangeStart->name() << ", encoding="
+ << llvm::format("0x%08x", unwindEntry.encoding));
+ if (unwindEntry.personalityFunction)
+ LLVM_DEBUG(llvm::dbgs()
+ << ", personality="
+ << unwindEntry.personalityFunction->name()
+ << ", lsdaLoc=" << unwindEntry.lsdaLocation->name());
+ LLVM_DEBUG(llvm::dbgs() << '\n');
+
+ // Count number of LSDAs we see, since we need to know how big the index
+ // will be while laying out the section.
+ if (unwindEntry.lsdaLocation)
+ ++numLSDAs;
+
+ // Gather the personality functions now, so that they're in deterministic
+ // order (derived from the DefinedAtom order).
+ if (unwindEntry.personalityFunction &&
+ !llvm::count(personalities, unwindEntry.personalityFunction))
+ personalities.push_back(unwindEntry.personalityFunction);
+ }
+ }
+
+ CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) {
+ CompactUnwindEntry entry;
+
+ for (const Reference *ref : *atom) {
+ switch (ref->offsetInAtom()) {
+ case 0:
+ // FIXME: there could legitimately be functions with multiple encoding
+ // entries. However, nothing produces them at the moment.
+ assert(ref->addend() == 0 && "unexpected offset into function");
+ entry.rangeStart = ref->target();
+ break;
+ case 0x10:
+ assert(ref->addend() == 0 && "unexpected offset into personality fn");
+ entry.personalityFunction = ref->target();
+ break;
+ case 0x18:
+ assert(ref->addend() == 0 && "unexpected offset into LSDA atom");
+ entry.lsdaLocation = ref->target();
+ break;
+ }
+ }
+
+ if (atom->rawContent().size() < 4 * sizeof(uint32_t))
+ return entry;
+
+ using normalized::read32;
+ entry.rangeLength =
+ read32(atom->rawContent().data() + 2 * sizeof(uint32_t), _isBig);
+ entry.encoding =
+ read32(atom->rawContent().data() + 3 * sizeof(uint32_t), _isBig);
+ return entry;
+ }
+
+ void
+ collectDwarfFrameEntries(const SimpleFile &mergedFile,
+ std::map<const Atom *, const Atom *> &dwarfFrames) {
+ for (const DefinedAtom *ehFrameAtom : mergedFile.defined()) {
+ if (ehFrameAtom->contentType() != DefinedAtom::typeCFI)
+ continue;
+ if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom))
+ continue;
+
+ if (const Atom *function = _archHandler.fdeTargetFunction(ehFrameAtom))
+ dwarfFrames[function] = ehFrameAtom;
+ }
+ }
+
+ /// Every atom defined in __TEXT,__text needs an entry in the final
+ /// __unwind_info section (in order). These comes from two sources:
+ /// + Input __compact_unwind sections where possible (after adding the
+ /// personality function offset which is only known now).
+ /// + A synthesised reference to __eh_frame if there's no __compact_unwind
+ /// or too many personality functions to be accommodated.
+ std::vector<CompactUnwindEntry> createUnwindInfoEntries(
+ const SimpleFile &mergedFile,
+ const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
+ const std::vector<const Atom *> &personalities,
+ const std::map<const Atom *, const Atom *> &dwarfFrames) {
+ std::vector<CompactUnwindEntry> unwindInfos;
+
+ LLVM_DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n");
+ // The final order in the __unwind_info section must be derived from the
+ // order of typeCode atoms, since that's how they'll be put into the object
+ // file eventually (yuck!).
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ if (atom->contentType() != DefinedAtom::typeCode)
+ continue;
+
+ unwindInfos.push_back(finalizeUnwindInfoEntryForAtom(
+ atom, unwindLocs, personalities, dwarfFrames));
+
+ LLVM_DEBUG(llvm::dbgs()
+ << " Entry for " << atom->name() << ", final encoding="
+ << llvm::format("0x%08x", unwindInfos.back().encoding)
+ << '\n');
+ }
+
+ return unwindInfos;
+ }
+
+ /// Remove unused EH frames.
+ ///
+ /// An EH frame is considered unused if there is a corresponding compact
+ /// unwind atom that doesn't require the EH frame.
+ void pruneUnusedEHFrames(
+ SimpleFile &mergedFile,
+ const std::vector<CompactUnwindEntry> &unwindInfos,
+ const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
+ const std::map<const Atom *, const Atom *> &dwarfFrames) {
+
+ // Worklist of all 'used' FDEs.
+ std::vector<const DefinedAtom *> usedDwarfWorklist;
+
+ // We have to check two conditions when building the worklist:
+ // (1) EH frames used by compact unwind entries.
+ for (auto &entry : unwindInfos)
+ if (entry.ehFrame)
+ usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.ehFrame));
+
+ // (2) EH frames that reference functions with no corresponding compact
+ // unwind info.
+ for (auto &entry : dwarfFrames)
+ if (!unwindLocs.count(entry.first))
+ usedDwarfWorklist.push_back(cast<DefinedAtom>(entry.second));
+
+ // Add all transitively referenced CFI atoms by processing the worklist.
+ std::set<const Atom *> usedDwarfFrames;
+ while (!usedDwarfWorklist.empty()) {
+ const DefinedAtom *cfiAtom = usedDwarfWorklist.back();
+ usedDwarfWorklist.pop_back();
+ usedDwarfFrames.insert(cfiAtom);
+ for (const auto *ref : *cfiAtom) {
+ const DefinedAtom *cfiTarget = dyn_cast<DefinedAtom>(ref->target());
+ if (cfiTarget->contentType() == DefinedAtom::typeCFI)
+ usedDwarfWorklist.push_back(cfiTarget);
+ }
+ }
+
+ // Finally, delete all unreferenced CFI atoms.
+ mergedFile.removeDefinedAtomsIf([&](const DefinedAtom *atom) {
+ if ((atom->contentType() == DefinedAtom::typeCFI) &&
+ !usedDwarfFrames.count(atom))
+ return true;
+ return false;
+ });
+ }
+
+ CompactUnwindEntry finalizeUnwindInfoEntryForAtom(
+ const DefinedAtom *function,
+ const std::map<const Atom *, CompactUnwindEntry> &unwindLocs,
+ const std::vector<const Atom *> &personalities,
+ const std::map<const Atom *, const Atom *> &dwarfFrames) {
+ auto unwindLoc = unwindLocs.find(function);
+
+ CompactUnwindEntry entry;
+ if (unwindLoc == unwindLocs.end()) {
+ // Default entry has correct encoding (0 => no unwind), but we need to
+ // synthesise the function.
+ entry.rangeStart = function;
+ entry.rangeLength = function->size();
+ } else
+ entry = unwindLoc->second;
+
+
+ // If there's no __compact_unwind entry, or it explicitly says to use
+ // __eh_frame, we need to try and fill in the correct DWARF atom.
+ if (entry.encoding == _archHandler.dwarfCompactUnwindType() ||
+ entry.encoding == 0) {
+ auto dwarfFrame = dwarfFrames.find(function);
+ if (dwarfFrame != dwarfFrames.end()) {
+ entry.encoding = _archHandler.dwarfCompactUnwindType();
+ entry.ehFrame = dwarfFrame->second;
+ }
+ }
+
+ auto personality = llvm::find(personalities, entry.personalityFunction);
+ uint32_t personalityIdx = personality == personalities.end()
+ ? 0
+ : personality - personalities.begin() + 1;
+
+ // FIXME: We should also use DWARF when there isn't enough room for the
+ // personality function in the compact encoding.
+ assert(personalityIdx < 4 && "too many personality functions");
+
+ entry.encoding |= personalityIdx << 28;
+
+ if (entry.lsdaLocation)
+ entry.encoding |= 1U << 30;
+
+ return entry;
+ }
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ MachOFile &_file;
+ bool _isBig;
+};
+
+void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ assert(ctx.needsCompactUnwindPass());
+ pm.add(std::make_unique<CompactUnwindPass>(ctx));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_DEBUGINFO_H
+#define LLD_READER_WRITER_MACHO_DEBUGINFO_H
+
+#include "lld/Core/Atom.h"
+#include <vector>
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+
+namespace lld {
+namespace mach_o {
+
+class DebugInfo {
+public:
+ enum class Kind {
+ Dwarf,
+ Stabs
+ };
+
+ Kind kind() const { return _kind; }
+
+ void setAllocator(std::unique_ptr<llvm::BumpPtrAllocator> allocator) {
+ _allocator = std::move(allocator);
+ }
+
+protected:
+ DebugInfo(Kind kind) : _kind(kind) {}
+
+private:
+ std::unique_ptr<llvm::BumpPtrAllocator> _allocator;
+ Kind _kind;
+};
+
+struct TranslationUnitSource {
+ StringRef name;
+ StringRef path;
+};
+
+class DwarfDebugInfo : public DebugInfo {
+public:
+ DwarfDebugInfo(TranslationUnitSource tu)
+ : DebugInfo(Kind::Dwarf), _tu(std::move(tu)) {}
+
+ static inline bool classof(const DebugInfo *di) {
+ return di->kind() == Kind::Dwarf;
+ }
+
+ const TranslationUnitSource &translationUnitSource() const { return _tu; }
+
+private:
+ TranslationUnitSource _tu;
+};
+
+struct Stab {
+ Stab(const Atom* atom, uint8_t type, uint8_t other, uint16_t desc,
+ uint32_t value, StringRef str)
+ : atom(atom), type(type), other(other), desc(desc), value(value),
+ str(str) {}
+
+ const class Atom* atom;
+ uint8_t type;
+ uint8_t other;
+ uint16_t desc;
+ uint32_t value;
+ StringRef str;
+};
+
+inline raw_ostream& operator<<(raw_ostream &os, Stab &s) {
+ os << "Stab -- atom: " << llvm::format("%p", s.atom) << ", type: " << (uint32_t)s.type
+ << ", other: " << (uint32_t)s.other << ", desc: " << s.desc << ", value: " << s.value
+ << ", str: '" << s.str << "'";
+ return os;
+}
+
+class StabsDebugInfo : public DebugInfo {
+public:
+
+ typedef std::vector<Stab> StabsList;
+
+ StabsDebugInfo(StabsList stabs)
+ : DebugInfo(Kind::Stabs), _stabs(std::move(stabs)) {}
+
+ static inline bool classof(const DebugInfo *di) {
+ return di->kind() == Kind::Stabs;
+ }
+
+ const StabsList& stabs() const { return _stabs; }
+
+public:
+ StabsList _stabs;
+};
+
+} // end namespace mach_o
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_DEBUGINFO_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/ExecutableAtoms.h ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
+#define LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
+
+#include "Atoms.h"
+#include "File.h"
+
+#include "llvm/BinaryFormat/MachO.h"
+
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "lld/Core/UndefinedAtom.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+
+namespace lld {
+namespace mach_o {
+
+
+//
+// CEntryFile adds an UndefinedAtom for "_main" so that the Resolving
+// phase will fail if "_main" is undefined.
+//
+class CEntryFile : public SimpleFile {
+public:
+ CEntryFile(const MachOLinkingContext &context)
+ : SimpleFile("C entry", kindCEntryObject),
+ _undefMain(*this, context.entrySymbolName()) {
+ this->addAtom(_undefMain);
+ }
+
+private:
+ SimpleUndefinedAtom _undefMain;
+};
+
+
+//
+// StubHelperFile adds an UndefinedAtom for "dyld_stub_binder" so that
+// the Resolveing phase will fail if "dyld_stub_binder" is undefined.
+//
+class StubHelperFile : public SimpleFile {
+public:
+ StubHelperFile(const MachOLinkingContext &context)
+ : SimpleFile("stub runtime", kindStubHelperObject),
+ _undefBinder(*this, context.binderSymbolName()) {
+ this->addAtom(_undefBinder);
+ }
+
+private:
+ SimpleUndefinedAtom _undefBinder;
+};
+
+
+//
+// MachHeaderAliasFile lazily instantiates the magic symbols that mark the start
+// of the mach_header for final linked images.
+//
+class MachHeaderAliasFile : public SimpleFile {
+public:
+ MachHeaderAliasFile(const MachOLinkingContext &context)
+ : SimpleFile("mach_header symbols", kindHeaderObject) {
+ StringRef machHeaderSymbolName;
+ DefinedAtom::Scope symbolScope = DefinedAtom::scopeLinkageUnit;
+ StringRef dsoHandleName;
+ switch (context.outputMachOType()) {
+ case llvm::MachO::MH_OBJECT:
+ machHeaderSymbolName = "__mh_object_header";
+ break;
+ case llvm::MachO::MH_EXECUTE:
+ machHeaderSymbolName = "__mh_execute_header";
+ symbolScope = DefinedAtom::scopeGlobal;
+ dsoHandleName = "___dso_handle";
+ break;
+ case llvm::MachO::MH_FVMLIB:
+ llvm_unreachable("no mach_header symbol for file type");
+ case llvm::MachO::MH_CORE:
+ llvm_unreachable("no mach_header symbol for file type");
+ case llvm::MachO::MH_PRELOAD:
+ llvm_unreachable("no mach_header symbol for file type");
+ case llvm::MachO::MH_DYLIB:
+ machHeaderSymbolName = "__mh_dylib_header";
+ dsoHandleName = "___dso_handle";
+ break;
+ case llvm::MachO::MH_DYLINKER:
+ machHeaderSymbolName = "__mh_dylinker_header";
+ dsoHandleName = "___dso_handle";
+ break;
+ case llvm::MachO::MH_BUNDLE:
+ machHeaderSymbolName = "__mh_bundle_header";
+ dsoHandleName = "___dso_handle";
+ break;
+ case llvm::MachO::MH_DYLIB_STUB:
+ llvm_unreachable("no mach_header symbol for file type");
+ case llvm::MachO::MH_DSYM:
+ llvm_unreachable("no mach_header symbol for file type");
+ case llvm::MachO::MH_KEXT_BUNDLE:
+ dsoHandleName = "___dso_handle";
+ break;
+ }
+ if (!machHeaderSymbolName.empty())
+ _definedAtoms.push_back(new (allocator()) MachODefinedAtom(
+ *this, machHeaderSymbolName, symbolScope,
+ DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false,
+ true /* noDeadStrip */,
+ ArrayRef<uint8_t>(), DefinedAtom::Alignment(4096)));
+
+ if (!dsoHandleName.empty())
+ _definedAtoms.push_back(new (allocator()) MachODefinedAtom(
+ *this, dsoHandleName, DefinedAtom::scopeLinkageUnit,
+ DefinedAtom::typeDSOHandle, DefinedAtom::mergeNo, false,
+ true /* noDeadStrip */,
+ ArrayRef<uint8_t>(), DefinedAtom::Alignment(1)));
+ }
+
+ const AtomRange<DefinedAtom> defined() const override {
+ return _definedAtoms;
+ }
+ const AtomRange<UndefinedAtom> undefined() const override {
+ return _noUndefinedAtoms;
+ }
+
+ const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
+ return _noSharedLibraryAtoms;
+ }
+
+ const AtomRange<AbsoluteAtom> absolute() const override {
+ return _noAbsoluteAtoms;
+ }
+
+ void clearAtoms() override {
+ _definedAtoms.clear();
+ _noUndefinedAtoms.clear();
+ _noSharedLibraryAtoms.clear();
+ _noAbsoluteAtoms.clear();
+ }
+
+
+private:
+ mutable AtomVector<DefinedAtom> _definedAtoms;
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_FILE_H
+#define LLD_READER_WRITER_MACHO_FILE_H
+
+#include "Atoms.h"
+#include "DebugInfo.h"
+#include "MachONormalizedFile.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Format.h"
+#include "llvm/TextAPI/InterfaceFile.h"
+#include "llvm/TextAPI/TextAPIReader.h"
+#include <unordered_map>
+
+namespace lld {
+namespace mach_o {
+
+using lld::mach_o::normalized::Section;
+
+class MachOFile : public SimpleFile {
+public:
+
+ /// Real file constructor - for on-disk files.
+ MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : SimpleFile(mb->getBufferIdentifier(), File::kindMachObject),
+ _mb(std::move(mb)), _ctx(ctx) {}
+
+ /// Dummy file constructor - for virtual files.
+ MachOFile(StringRef path)
+ : SimpleFile(path, File::kindMachObject) {}
+
+ void addDefinedAtom(StringRef name, Atom::Scope scope,
+ DefinedAtom::ContentType type, DefinedAtom::Merge merge,
+ uint64_t sectionOffset, uint64_t contentSize, bool thumb,
+ bool noDeadStrip, bool copyRefs,
+ const Section *inSection) {
+ assert(sectionOffset+contentSize <= inSection->content.size());
+ ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
+ contentSize);
+ if (copyRefs) {
+ // Make a copy of the atom's name and content that is owned by this file.
+ name = name.copy(allocator());
+ content = content.copy(allocator());
+ }
+ DefinedAtom::Alignment align(
+ inSection->alignment,
+ sectionOffset % inSection->alignment);
+ auto *atom =
+ new (allocator()) MachODefinedAtom(*this, name, scope, type, merge,
+ thumb, noDeadStrip, content, align);
+ addAtomForSection(inSection, atom, sectionOffset);
+ }
+
+ void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope,
+ DefinedAtom::ContentType type, DefinedAtom::Merge merge,
+ bool thumb, bool noDeadStrip, uint64_t sectionOffset,
+ uint64_t contentSize, StringRef sectionName,
+ bool copyRefs, const Section *inSection) {
+ assert(sectionOffset+contentSize <= inSection->content.size());
+ ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset,
+ contentSize);
+ if (copyRefs) {
+ // Make a copy of the atom's name and content that is owned by this file.
+ name = name.copy(allocator());
+ content = content.copy(allocator());
+ sectionName = sectionName.copy(allocator());
+ }
+ DefinedAtom::Alignment align(
+ inSection->alignment,
+ sectionOffset % inSection->alignment);
+ auto *atom =
+ new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type,
+ merge, thumb,
+ noDeadStrip, content,
+ sectionName, align);
+ addAtomForSection(inSection, atom, sectionOffset);
+ }
+
+ void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope,
+ uint64_t sectionOffset, uint64_t size,
+ bool noDeadStrip, bool copyRefs,
+ const Section *inSection) {
+ if (copyRefs) {
+ // Make a copy of the atom's name and content that is owned by this file.
+ name = name.copy(allocator());
+ }
+ DefinedAtom::Alignment align(
+ inSection->alignment,
+ sectionOffset % inSection->alignment);
+
+ DefinedAtom::ContentType type = DefinedAtom::typeUnknown;
+ switch (inSection->type) {
+ case llvm::MachO::S_ZEROFILL:
+ type = DefinedAtom::typeZeroFill;
+ break;
+ case llvm::MachO::S_THREAD_LOCAL_ZEROFILL:
+ type = DefinedAtom::typeTLVInitialZeroFill;
+ break;
+ default:
+ llvm_unreachable("Unrecognized zero-fill section");
+ }
+
+ auto *atom =
+ new (allocator()) MachODefinedAtom(*this, name, scope, type, size,
+ noDeadStrip, align);
+ addAtomForSection(inSection, atom, sectionOffset);
+ }
+
+ void addUndefinedAtom(StringRef name, bool copyRefs) {
+ if (copyRefs) {
+ // Make a copy of the atom's name that is owned by this file.
+ name = name.copy(allocator());
+ }
+ auto *atom = new (allocator()) SimpleUndefinedAtom(*this, name);
+ addAtom(*atom);
+ _undefAtoms[name] = atom;
+ }
+
+ void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size,
+ DefinedAtom::Alignment align, bool copyRefs) {
+ if (copyRefs) {
+ // Make a copy of the atom's name that is owned by this file.
+ name = name.copy(allocator());
+ }
+ auto *atom =
+ new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align);
+ addAtom(*atom);
+ _undefAtoms[name] = atom;
+ }
+
+ /// Search this file for the atom from 'section' that covers
+ /// 'offsetInSect'. Returns nullptr is no atom found.
+ MachODefinedAtom *findAtomCoveringAddress(const Section §ion,
+ uint64_t offsetInSect,
+ uint32_t *foundOffsetAtom=nullptr) {
+ const auto &pos = _sectionAtoms.find(§ion);
+ if (pos == _sectionAtoms.end())
+ return nullptr;
+ const auto &vec = pos->second;
+ assert(offsetInSect < section.content.size());
+ // Vector of atoms for section are already sorted, so do binary search.
+ const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect,
+ [offsetInSect](const SectionOffsetAndAtom &ao,
+ uint64_t targetAddr) -> bool {
+ // Each atom has a start offset of its slice of the
+ // section's content. This compare function must return true
+ // iff the atom's range is before the offset being searched for.
+ uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size();
+ return (atomsEndOffset <= offsetInSect);
+ });
+ if (atomPos == vec.end())
+ return nullptr;
+ if (foundOffsetAtom)
+ *foundOffsetAtom = offsetInSect - atomPos->offset;
+ return atomPos->atom;
+ }
+
+ /// Searches this file for an UndefinedAtom named 'name'. Returns
+ /// nullptr is no such atom found.
+ const lld::Atom *findUndefAtom(StringRef name) {
+ auto pos = _undefAtoms.find(name);
+ if (pos == _undefAtoms.end())
+ return nullptr;
+ return pos->second;
+ }
+
+ typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor;
+
+ void eachDefinedAtom(DefinedAtomVisitor vistor) {
+ for (auto §AndAtoms : _sectionAtoms) {
+ for (auto &offAndAtom : sectAndAtoms.second) {
+ vistor(offAndAtom.atom);
+ }
+ }
+ }
+
+ typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)>
+ SectionAtomVisitor;
+
+ void eachAtomInSection(const Section §ion, SectionAtomVisitor visitor) {
+ auto pos = _sectionAtoms.find(§ion);
+ if (pos == _sectionAtoms.end())
+ return;
+ auto vec = pos->second;
+
+ for (auto &offAndAtom : vec)
+ visitor(offAndAtom.atom, offAndAtom.offset);
+ }
+
+ MachOLinkingContext::Arch arch() const { return _arch; }
+ void setArch(MachOLinkingContext::Arch arch) { _arch = arch; }
+
+ MachOLinkingContext::OS OS() const { return _os; }
+ void setOS(MachOLinkingContext::OS os) { _os = os; }
+
+ MachOLinkingContext::ObjCConstraint objcConstraint() const {
+ return _objcConstraint;
+ }
+ void setObjcConstraint(MachOLinkingContext::ObjCConstraint v) {
+ _objcConstraint = v;
+ }
+
+ uint32_t minVersion() const { return _minVersion; }
+ void setMinVersion(uint32_t v) { _minVersion = v; }
+
+ LoadCommandType minVersionLoadCommandKind() const {
+ return _minVersionLoadCommandKind;
+ }
+ void setMinVersionLoadCommandKind(LoadCommandType v) {
+ _minVersionLoadCommandKind = v;
+ }
+
+ uint32_t swiftVersion() const { return _swiftVersion; }
+ void setSwiftVersion(uint32_t v) { _swiftVersion = v; }
+
+ bool subsectionsViaSymbols() const {
+ return _flags & llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ }
+ void setFlags(normalized::FileFlags v) { _flags = v; }
+
+ /// Methods for support type inquiry through isa, cast, and dyn_cast:
+ static inline bool classof(const File *F) {
+ return F->kind() == File::kindMachObject;
+ }
+
+ void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) {
+ _debugInfo = std::move(debugInfo);
+ }
+
+ DebugInfo* debugInfo() const { return _debugInfo.get(); }
+ std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); }
+
+protected:
+ std::error_code doParse() override {
+ // Convert binary file to normalized mach-o.
+ auto normFile = normalized::readBinary(_mb, _ctx->arch());
+ if (auto ec = normFile.takeError())
+ return llvm::errorToErrorCode(std::move(ec));
+ // Convert normalized mach-o to atoms.
+ if (auto ec = normalized::normalizedObjectToAtoms(this, **normFile, false))
+ return llvm::errorToErrorCode(std::move(ec));
+ return std::error_code();
+ }
+
+private:
+ struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; };
+
+ void addAtomForSection(const Section *inSection, MachODefinedAtom* atom,
+ uint64_t sectionOffset) {
+ SectionOffsetAndAtom offAndAtom;
+ offAndAtom.offset = sectionOffset;
+ offAndAtom.atom = atom;
+ _sectionAtoms[inSection].push_back(offAndAtom);
+ addAtom(*atom);
+ }
+
+ typedef llvm::DenseMap<const normalized::Section *,
+ std::vector<SectionOffsetAndAtom>> SectionToAtoms;
+ typedef llvm::StringMap<const lld::Atom *> NameToAtom;
+
+ std::unique_ptr<MemoryBuffer> _mb;
+ MachOLinkingContext *_ctx;
+ SectionToAtoms _sectionAtoms;
+ NameToAtom _undefAtoms;
+ MachOLinkingContext::Arch _arch = MachOLinkingContext::arch_unknown;
+ MachOLinkingContext::OS _os = MachOLinkingContext::OS::unknown;
+ uint32_t _minVersion = 0;
+ LoadCommandType _minVersionLoadCommandKind = (LoadCommandType)0;
+ MachOLinkingContext::ObjCConstraint _objcConstraint =
+ MachOLinkingContext::objc_unknown;
+ uint32_t _swiftVersion = 0;
+ normalized::FileFlags _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ std::unique_ptr<DebugInfo> _debugInfo;
+};
+
+class MachODylibFile : public SharedLibraryFile {
+public:
+ MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : SharedLibraryFile(mb->getBufferIdentifier()),
+ _mb(std::move(mb)), _ctx(ctx) {}
+
+ MachODylibFile(StringRef path) : SharedLibraryFile(path) {}
+
+ OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
+ // Pass down _installName so that if this requested symbol
+ // is re-exported through this dylib, the SharedLibraryAtom's loadName()
+ // is this dylib installName and not the implementation dylib's.
+ // NOTE: isData is not needed for dylibs (it matters for static libs).
+ return exports(name, _installName);
+ }
+
+ /// Adds symbol name that this dylib exports. The corresponding
+ /// SharedLibraryAtom is created lazily (since most symbols are not used).
+ void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) {
+ if (copyRefs) {
+ name = name.copy(allocator());
+ }
+ AtomAndFlags info(weakDef);
+ _nameToAtom[name] = info;
+ }
+
+ void addReExportedDylib(StringRef dylibPath) {
+ _reExportedDylibs.emplace_back(dylibPath);
+ }
+
+ StringRef installName() const { return _installName; }
+ uint32_t currentVersion() { return _currentVersion; }
+ uint32_t compatVersion() { return _compatVersion; }
+
+ void setInstallName(StringRef name) { _installName = name; }
+ void setCompatVersion(uint32_t version) { _compatVersion = version; }
+ void setCurrentVersion(uint32_t version) { _currentVersion = version; }
+
+ typedef std::function<MachODylibFile *(StringRef)> FindDylib;
+
+ void loadReExportedDylibs(FindDylib find) {
+ for (ReExportedDylib &entry : _reExportedDylibs) {
+ if (!entry.file)
+ entry.file = find(entry.path);
+ }
+ }
+
+ StringRef getDSOName() const override { return _installName; }
+
+ std::error_code doParse() override {
+ // Convert binary file to normalized mach-o.
+ auto normFile = normalized::readBinary(_mb, _ctx->arch());
+ if (auto ec = normFile.takeError())
+ return llvm::errorToErrorCode(std::move(ec));
+ // Convert normalized mach-o to atoms.
+ if (auto ec = normalized::normalizedDylibToAtoms(this, **normFile, false))
+ return llvm::errorToErrorCode(std::move(ec));
+ return std::error_code();
+ }
+
+protected:
+ OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
+ StringRef installName) const {
+ // First, check if requested symbol is directly implemented by this dylib.
+ auto entry = _nameToAtom.find(name);
+ if (entry != _nameToAtom.end()) {
+ // FIXME: Make this map a set and only used in assert builds.
+ // Note, its safe to assert here as the resolver is the only client of
+ // this API and it only requests exports for undefined symbols.
+ // If we return from here we are no longer undefined so we should never
+ // get here again.
+ assert(!entry->second.atom && "Duplicate shared library export");
+ bool weakDef = entry->second.weakDef;
+ auto *atom = new (allocator()) MachOSharedLibraryAtom(*this, name,
+ installName,
+ weakDef);
+ entry->second.atom = atom;
+ return atom;
+ }
+
+ // Next, check if symbol is implemented in some re-exported dylib.
+ for (const ReExportedDylib &dylib : _reExportedDylibs) {
+ assert(dylib.file);
+ auto atom = dylib.file->exports(name, installName);
+ if (atom.get())
+ return atom;
+ }
+
+ // Symbol not exported or re-exported by this dylib.
+ return nullptr;
+ }
+
+ struct ReExportedDylib {
+ ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
+ ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
+ StringRef path;
+ MachODylibFile *file;
+ };
+
+ struct AtomAndFlags {
+ AtomAndFlags() : atom(nullptr), weakDef(false) { }
+ AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { }
+ const SharedLibraryAtom *atom;
+ bool weakDef;
+ };
+
+ std::unique_ptr<MemoryBuffer> _mb;
+ MachOLinkingContext *_ctx;
+ StringRef _installName;
+ uint32_t _currentVersion;
+ uint32_t _compatVersion;
+ std::vector<ReExportedDylib> _reExportedDylibs;
+ mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
+};
+
+class TAPIFile : public MachODylibFile {
+public:
+
+ TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : MachODylibFile(std::move(mb), ctx) {}
+
+ std::error_code doParse() override {
+
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ llvm::MachO::TextAPIReader::get(*_mb);
+ if (!result)
+ return std::make_error_code(std::errc::invalid_argument);
+
+ std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
+ return loadFromInterface(*interface);
+ }
+
+private:
+ std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
+ llvm::MachO::Architecture arch;
+ switch(_ctx->arch()) {
+ case MachOLinkingContext::arch_x86:
+ arch = llvm::MachO::AK_i386;
+ break;
+ case MachOLinkingContext::arch_x86_64:
+ arch = llvm::MachO::AK_x86_64;
+ break;
+ case MachOLinkingContext::arch_arm64:
+ arch = llvm::MachO::AK_arm64;
+ break;
+ default:
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+
+ setInstallName(interface.getInstallName().copy(allocator()));
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface.symbols())
+ if (symbol->getArchitectures().has(arch))
+ addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
+
+ for (const llvm::MachO::InterfaceFileRef &reexport :
+ interface.reexportedLibraries())
+ addReExportedDylib(reexport.getInstallName().copy(allocator()));
+
+ for (const auto& document : interface.documents()) {
+ for (auto& reexport : _reExportedDylibs) {
+ if (reexport.path != document->getInstallName())
+ continue;
+ assert(!reexport.file);
+ _ownedFiles.push_back(std::make_unique<TAPIFile>(
+ MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
+ reexport.file = _ownedFiles.back().get();
+ std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
+ if (err)
+ return err;
+ }
+ }
+
+ return std::error_code();
+ }
+
+ std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
+};
+
+} // end namespace mach_o
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_FILE_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/FlatNamespaceFile.h -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
+#define LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
+
+#include "Atoms.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/Support/Debug.h"
+
+namespace lld {
+namespace mach_o {
+
+//
+// A FlateNamespaceFile instance may be added as a resolution source of last
+// resort, depending on how -flat_namespace and -undefined are set.
+//
+class FlatNamespaceFile : public SharedLibraryFile {
+public:
+ FlatNamespaceFile(const MachOLinkingContext &context)
+ : SharedLibraryFile("flat namespace") { }
+
+ OwningAtomPtr<SharedLibraryAtom> exports(StringRef name) const override {
+ return new (allocator()) MachOSharedLibraryAtom(*this, name, getDSOName(),
+ false);
+ }
+
+ StringRef getDSOName() const override { return "flat-namespace"; }
+
+ const AtomRange<DefinedAtom> defined() const override {
+ return _noDefinedAtoms;
+ }
+ const AtomRange<UndefinedAtom> undefined() const override {
+ return _noUndefinedAtoms;
+ }
+
+ const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
+ return _noSharedLibraryAtoms;
+ }
+
+ const AtomRange<AbsoluteAtom> absolute() const override {
+ return _noAbsoluteAtoms;
+ }
+
+ void clearAtoms() override {
+ _noDefinedAtoms.clear();
+ _noUndefinedAtoms.clear();
+ _noSharedLibraryAtoms.clear();
+ _noAbsoluteAtoms.clear();
+ }
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/GOTPass.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This linker pass transforms all GOT kind references to real references.
+/// That is, in assembly you can write something like:
+/// movq foo@GOTPCREL(%rip), %rax
+/// which means you want to load a pointer to "foo" out of the GOT (global
+/// Offsets Table). In the object file, the Atom containing this instruction
+/// has a Reference whose target is an Atom named "foo" and the Reference
+/// kind is a GOT load. The linker needs to instantiate a pointer sized
+/// GOT entry. This is done be creating a GOT Atom to represent that pointer
+/// sized data in this pass, and altering the Atom graph so the Reference now
+/// points to the GOT Atom entry (corresponding to "foo") and changing the
+/// Reference Kind to reflect it is now pointing to a GOT entry (rather
+/// then needing a GOT entry).
+///
+/// There is one optimization the linker can do here. If the target of the GOT
+/// is in the same linkage unit and does not need to be interposable, and
+/// the GOT use is just a load (not some other operation), this pass can
+/// transform that load into an LEA (add). This optimizes away one memory load
+/// which at runtime that could stall the pipeline. This optimization only
+/// works for architectures in which a (GOT) load instruction can be change to
+/// an LEA instruction that is the same size. The method isGOTAccess() should
+/// only return true for "canBypassGOT" if this optimization is supported.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace lld {
+namespace mach_o {
+
+//
+// GOT Entry Atom created by the GOT pass.
+//
+class GOTEntryAtom : public SimpleDefinedAtom {
+public:
+ GOTEntryAtom(const File &file, bool is64, StringRef name)
+ : SimpleDefinedAtom(file), _is64(is64), _name(name) { }
+
+ ~GOTEntryAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeGOT;
+ }
+
+ Alignment alignment() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ uint64_t size() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permRW_;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t zeros[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ return llvm::makeArrayRef(zeros, size());
+ }
+
+ StringRef slotName() const {
+ return _name;
+ }
+
+private:
+ const bool _is64;
+ StringRef _name;
+};
+
+/// Pass for instantiating and optimizing GOT slots.
+///
+class GOTPass : public Pass {
+public:
+ GOTPass(const MachOLinkingContext &context)
+ : _ctx(context), _archHandler(_ctx.archHandler()),
+ _file(*_ctx.make_file<MachOFile>("<mach-o GOT Pass>")) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+private:
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ // Scan all references in all atoms.
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ for (const Reference *ref : *atom) {
+ // Look at instructions accessing the GOT.
+ bool canBypassGOT;
+ if (!_archHandler.isGOTAccess(*ref, canBypassGOT))
+ continue;
+ const Atom *target = ref->target();
+ assert(target != nullptr);
+
+ if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) {
+ // Update reference kind to reflect that target is a direct access.
+ _archHandler.updateReferenceToGOT(ref, false);
+ } else {
+ // Replace the target with a reference to a GOT entry.
+ const DefinedAtom *gotEntry = makeGOTEntry(target);
+ const_cast<Reference *>(ref)->setTarget(gotEntry);
+ // Update reference kind to reflect that target is now a GOT entry.
+ _archHandler.updateReferenceToGOT(ref, true);
+ }
+ }
+ }
+
+ // Sort and add all created GOT Atoms to master file
+ std::vector<const GOTEntryAtom *> entries;
+ entries.reserve(_targetToGOT.size());
+ for (auto &it : _targetToGOT)
+ entries.push_back(it.second);
+ std::sort(entries.begin(), entries.end(),
+ [](const GOTEntryAtom *left, const GOTEntryAtom *right) {
+ return (left->slotName().compare(right->slotName()) < 0);
+ });
+ for (const GOTEntryAtom *slot : entries)
+ mergedFile.addAtom(*slot);
+
+ return llvm::Error::success();
+ }
+
+ bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) {
+ // Accesses to shared library symbols must go through GOT.
+ if (isa<SharedLibraryAtom>(target))
+ return true;
+ // Accesses to interposable symbols in same linkage unit must also go
+ // through GOT.
+ const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
+ if (defTarget != nullptr &&
+ defTarget->interposable() != DefinedAtom::interposeNo) {
+ assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
+ return true;
+ }
+ // Target does not require indirection. So, if instruction allows GOT to be
+ // by-passed, do that optimization and don't create GOT entry.
+ return !canBypassGOT;
+ }
+
+ const DefinedAtom *makeGOTEntry(const Atom *target) {
+ auto pos = _targetToGOT.find(target);
+ if (pos == _targetToGOT.end()) {
+ auto *gotEntry = new (_file.allocator())
+ GOTEntryAtom(_file, _ctx.is64Bit(), target->name());
+ _targetToGOT[target] = gotEntry;
+ const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo().
+ nonLazyPointerReferenceToBinder;
+ gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
+ nlInfo.kind, 0, target, 0);
+ return gotEntry;
+ }
+ return pos->second;
+ }
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ MachOFile &_file;
+ llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT;
+};
+
+void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ assert(ctx.needsGOTPass());
+ pm.add(std::make_unique<GOTPass>(ctx));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LayoutPass.h"
+#include "lld/Core/Instrumentation.h"
+#include "lld/Core/PassManager.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Parallel.h"
+#include <algorithm>
+#include <set>
+#include <utility>
+
+using namespace lld;
+
+#define DEBUG_TYPE "LayoutPass"
+
+namespace lld {
+namespace mach_o {
+
+static bool compareAtoms(const LayoutPass::SortKey &,
+ const LayoutPass::SortKey &,
+ LayoutPass::SortOverride customSorter);
+
+#ifndef NDEBUG
+// Return "reason (leftval, rightval)"
+static std::string formatReason(StringRef reason, int leftVal, int rightVal) {
+ return (Twine(reason) + " (" + Twine(leftVal) + ", " + Twine(rightVal) + ")")
+ .str();
+}
+
+// Less-than relationship of two atoms must be transitive, which is, if a < b
+// and b < c, a < c must be true. This function checks the transitivity by
+// checking the sort results.
+static void checkTransitivity(std::vector<LayoutPass::SortKey> &vec,
+ LayoutPass::SortOverride customSorter) {
+ for (auto i = vec.begin(), e = vec.end(); (i + 1) != e; ++i) {
+ for (auto j = i + 1; j != e; ++j) {
+ assert(compareAtoms(*i, *j, customSorter));
+ assert(!compareAtoms(*j, *i, customSorter));
+ }
+ }
+}
+
+// Helper functions to check follow-on graph.
+typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
+
+static std::string atomToDebugString(const Atom *atom) {
+ const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom);
+ std::string str;
+ llvm::raw_string_ostream s(str);
+ if (definedAtom->name().empty())
+ s << "<anonymous " << definedAtom << ">";
+ else
+ s << definedAtom->name();
+ s << " in ";
+ if (definedAtom->customSectionName().empty())
+ s << "<anonymous>";
+ else
+ s << definedAtom->customSectionName();
+ s.flush();
+ return str;
+}
+
+static void showCycleDetectedError(const Registry ®istry,
+ AtomToAtomT &followOnNexts,
+ const DefinedAtom *atom) {
+ const DefinedAtom *start = atom;
+ llvm::dbgs() << "There's a cycle in a follow-on chain!\n";
+ do {
+ llvm::dbgs() << " " << atomToDebugString(atom) << "\n";
+ for (const Reference *ref : *atom) {
+ StringRef kindValStr;
+ if (!registry.referenceKindToString(ref->kindNamespace(), ref->kindArch(),
+ ref->kindValue(), kindValStr)) {
+ kindValStr = "<unknown>";
+ }
+ llvm::dbgs() << " " << kindValStr
+ << ": " << atomToDebugString(ref->target()) << "\n";
+ }
+ atom = followOnNexts[atom];
+ } while (atom != start);
+ llvm::report_fatal_error("Cycle detected");
+}
+
+/// Exit if there's a cycle in a followon chain reachable from the
+/// given root atom. Uses the tortoise and hare algorithm to detect a
+/// cycle.
+static void checkNoCycleInFollowonChain(const Registry ®istry,
+ AtomToAtomT &followOnNexts,
+ const DefinedAtom *root) {
+ const DefinedAtom *tortoise = root;
+ const DefinedAtom *hare = followOnNexts[root];
+ while (true) {
+ if (!tortoise || !hare)
+ return;
+ if (tortoise == hare)
+ showCycleDetectedError(registry, followOnNexts, tortoise);
+ tortoise = followOnNexts[tortoise];
+ hare = followOnNexts[followOnNexts[hare]];
+ }
+}
+
+static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots,
+ const DefinedAtom *atom) {
+ if (!atom) return;
+ auto i = followOnRoots.find(atom);
+ if (i == followOnRoots.end()) {
+ llvm_unreachable(((Twine("Atom <") + atomToDebugString(atom) +
+ "> has no follow-on root!"))
+ .str()
+ .c_str());
+ }
+ const DefinedAtom *ap = i->second;
+ while (true) {
+ const DefinedAtom *next = followOnRoots[ap];
+ if (!next) {
+ llvm_unreachable((Twine("Atom <" + atomToDebugString(atom) +
+ "> is not reachable from its root!"))
+ .str()
+ .c_str());
+ }
+ if (next == ap)
+ return;
+ ap = next;
+ }
+}
+
+static void printDefinedAtoms(const File::AtomRange<DefinedAtom> &atomRange) {
+ for (const DefinedAtom *atom : atomRange) {
+ llvm::dbgs() << " file=" << atom->file().path()
+ << ", name=" << atom->name()
+ << ", size=" << atom->size()
+ << ", type=" << atom->contentType()
+ << ", ordinal=" << atom->ordinal()
+ << "\n";
+ }
+}
+
+/// Verify that the followon chain is sane. Should not be called in
+/// release binary.
+void LayoutPass::checkFollowonChain(const File::AtomRange<DefinedAtom> &range) {
+ ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain");
+
+ // Verify that there's no cycle in follow-on chain.
+ std::set<const DefinedAtom *> roots;
+ for (const auto &ai : _followOnRoots)
+ roots.insert(ai.second);
+ for (const DefinedAtom *root : roots)
+ checkNoCycleInFollowonChain(_registry, _followOnNexts, root);
+
+ // Verify that all the atoms in followOnNexts have references to
+ // their roots.
+ for (const auto &ai : _followOnNexts) {
+ checkReachabilityFromRoot(_followOnRoots, ai.first);
+ checkReachabilityFromRoot(_followOnRoots, ai.second);
+ }
+}
+#endif // #ifndef NDEBUG
+
+/// The function compares atoms by sorting atoms in the following order
+/// a) Sorts atoms by their ordinal overrides (layout-after/ingroup)
+/// b) Sorts atoms by their permissions
+/// c) Sorts atoms by their content
+/// d) Sorts atoms by custom sorter
+/// e) Sorts atoms on how they appear using File Ordinality
+/// f) Sorts atoms on how they appear within the File
+static bool compareAtomsSub(const LayoutPass::SortKey &lc,
+ const LayoutPass::SortKey &rc,
+ LayoutPass::SortOverride customSorter,
+ std::string &reason) {
+ const DefinedAtom *left = lc._atom.get();
+ const DefinedAtom *right = rc._atom.get();
+ if (left == right) {
+ reason = "same";
+ return false;
+ }
+
+ // Find the root of the chain if it is a part of a follow-on chain.
+ const DefinedAtom *leftRoot = lc._root;
+ const DefinedAtom *rightRoot = rc._root;
+
+ // Sort atoms by their ordinal overrides only if they fall in the same
+ // chain.
+ if (leftRoot == rightRoot) {
+ LLVM_DEBUG(reason = formatReason("override", lc._override, rc._override));
+ return lc._override < rc._override;
+ }
+
+ // Sort same permissions together.
+ DefinedAtom::ContentPermissions leftPerms = leftRoot->permissions();
+ DefinedAtom::ContentPermissions rightPerms = rightRoot->permissions();
+
+ if (leftPerms != rightPerms) {
+ LLVM_DEBUG(
+ reason = formatReason("contentPerms", (int)leftPerms, (int)rightPerms));
+ return leftPerms < rightPerms;
+ }
+
+ // Sort same content types together.
+ DefinedAtom::ContentType leftType = leftRoot->contentType();
+ DefinedAtom::ContentType rightType = rightRoot->contentType();
+
+ if (leftType != rightType) {
+ LLVM_DEBUG(reason =
+ formatReason("contentType", (int)leftType, (int)rightType));
+ return leftType < rightType;
+ }
+
+ // Use custom sorter if supplied.
+ if (customSorter) {
+ bool leftBeforeRight;
+ if (customSorter(leftRoot, rightRoot, leftBeforeRight))
+ return leftBeforeRight;
+ }
+
+ // Sort by .o order.
+ const File *leftFile = &leftRoot->file();
+ const File *rightFile = &rightRoot->file();
+
+ if (leftFile != rightFile) {
+ LLVM_DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(),
+ (int)rightFile->ordinal()));
+ return leftFile->ordinal() < rightFile->ordinal();
+ }
+
+ // Sort by atom order with .o file.
+ uint64_t leftOrdinal = leftRoot->ordinal();
+ uint64_t rightOrdinal = rightRoot->ordinal();
+
+ if (leftOrdinal != rightOrdinal) {
+ LLVM_DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(),
+ (int)rightRoot->ordinal()));
+ return leftOrdinal < rightOrdinal;
+ }
+
+ llvm::errs() << "Unordered: <" << left->name() << "> <" << right->name()
+ << ">\n";
+ llvm_unreachable("Atoms with Same Ordinal!");
+}
+
+static bool compareAtoms(const LayoutPass::SortKey &lc,
+ const LayoutPass::SortKey &rc,
+ LayoutPass::SortOverride customSorter) {
+ std::string reason;
+ bool result = compareAtomsSub(lc, rc, customSorter, reason);
+ LLVM_DEBUG({
+ StringRef comp = result ? "<" : ">=";
+ llvm::dbgs() << "Layout: '" << lc._atom.get()->name()
+ << "' " << comp << " '"
+ << rc._atom.get()->name() << "' (" << reason << ")\n";
+ });
+ return result;
+}
+
+LayoutPass::LayoutPass(const Registry ®istry, SortOverride sorter)
+ : _registry(registry), _customSorter(std::move(sorter)) {}
+
+// Returns the atom immediately followed by the given atom in the followon
+// chain.
+const DefinedAtom *LayoutPass::findAtomFollowedBy(
+ const DefinedAtom *targetAtom) {
+ // Start from the beginning of the chain and follow the chain until
+ // we find the targetChain.
+ const DefinedAtom *atom = _followOnRoots[targetAtom];
+ while (true) {
+ const DefinedAtom *prevAtom = atom;
+ AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
+ // The target atom must be in the chain of its root.
+ assert(targetFollowOnAtomsIter != _followOnNexts.end());
+ atom = targetFollowOnAtomsIter->second;
+ if (atom == targetAtom)
+ return prevAtom;
+ }
+}
+
+// Check if all the atoms followed by the given target atom are of size zero.
+// When this method is called, an atom being added is not of size zero and
+// will be added to the head of the followon chain. All the atoms between the
+// atom and the targetAtom (specified by layout-after) need to be of size zero
+// in this case. Otherwise the desired layout is impossible.
+bool LayoutPass::checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom) {
+ const DefinedAtom *atom = _followOnRoots[targetAtom];
+ while (true) {
+ if (atom == targetAtom)
+ return true;
+ if (atom->size() != 0)
+ // TODO: print warning that an impossible layout is being desired by the
+ // user.
+ return false;
+ AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom);
+ // The target atom must be in the chain of its root.
+ assert(targetFollowOnAtomsIter != _followOnNexts.end());
+ atom = targetFollowOnAtomsIter->second;
+ }
+}
+
+// Set the root of all atoms in targetAtom's chain to the given root.
+void LayoutPass::setChainRoot(const DefinedAtom *targetAtom,
+ const DefinedAtom *root) {
+ // Walk through the followon chain and override each node's root.
+ while (true) {
+ _followOnRoots[targetAtom] = root;
+ AtomToAtomT::iterator targetFollowOnAtomsIter =
+ _followOnNexts.find(targetAtom);
+ if (targetFollowOnAtomsIter == _followOnNexts.end())
+ return;
+ targetAtom = targetFollowOnAtomsIter->second;
+ }
+}
+
+/// This pass builds the followon tables described by two DenseMaps
+/// followOnRoots and followonNexts.
+/// The followOnRoots map contains a mapping of a DefinedAtom to its root
+/// The followOnNexts map contains a mapping of what DefinedAtom follows the
+/// current Atom
+/// The algorithm follows a very simple approach
+/// a) If the atom is first seen, then make that as the root atom
+/// b) The targetAtom which this Atom contains, has the root thats set to the
+/// root of the current atom
+/// c) If the targetAtom is part of a different tree and the root of the
+/// targetAtom is itself, Chain all the atoms that are contained in the tree
+/// to the current Tree
+/// d) If the targetAtom is part of a different chain and the root of the
+/// targetAtom until the targetAtom has all atoms of size 0, then chain the
+/// targetAtoms and its tree to the current chain
+void LayoutPass::buildFollowOnTable(const File::AtomRange<DefinedAtom> &range) {
+ ScopedTask task(getDefaultDomain(), "LayoutPass::buildFollowOnTable");
+ // Set the initial size of the followon and the followonNext hash to the
+ // number of atoms that we have.
+ _followOnRoots.reserve(range.size());
+ _followOnNexts.reserve(range.size());
+ for (const DefinedAtom *ai : range) {
+ for (const Reference *r : *ai) {
+ if (r->kindNamespace() != lld::Reference::KindNamespace::all ||
+ r->kindValue() != lld::Reference::kindLayoutAfter)
+ continue;
+ const DefinedAtom *targetAtom = dyn_cast<DefinedAtom>(r->target());
+ _followOnNexts[ai] = targetAtom;
+
+ // If we find a followon for the first time, let's make that atom as the
+ // root atom.
+ if (_followOnRoots.count(ai) == 0)
+ _followOnRoots[ai] = ai;
+
+ auto iter = _followOnRoots.find(targetAtom);
+ if (iter == _followOnRoots.end()) {
+ // If the targetAtom is not a root of any chain, let's make the root of
+ // the targetAtom to the root of the current chain.
+
+ // The expression m[i] = m[j] where m is a DenseMap and i != j is not
+ // safe. m[j] returns a reference, which would be invalidated when a
+ // rehashing occurs. If rehashing occurs to make room for m[i], m[j]
+ // becomes invalid, and that invalid reference would be used as the RHS
+ // value of the expression.
+ // Copy the value to workaround.
+ const DefinedAtom *tmp = _followOnRoots[ai];
+ _followOnRoots[targetAtom] = tmp;
+ continue;
+ }
+ if (iter->second == targetAtom) {
+ // If the targetAtom is the root of a chain, the chain becomes part of
+ // the current chain. Rewrite the subchain's root to the current
+ // chain's root.
+ setChainRoot(targetAtom, _followOnRoots[ai]);
+ continue;
+ }
+ // The targetAtom is already a part of a chain. If the current atom is
+ // of size zero, we can insert it in the middle of the chain just
+ // before the target atom, while not breaking other atom's followon
+ // relationships. If it's not, we can only insert the current atom at
+ // the beginning of the chain. All the atoms followed by the target
+ // atom must be of size zero in that case to satisfy the followon
+ // relationships.
+ size_t currentAtomSize = ai->size();
+ if (currentAtomSize == 0) {
+ const DefinedAtom *targetPrevAtom = findAtomFollowedBy(targetAtom);
+ _followOnNexts[targetPrevAtom] = ai;
+ const DefinedAtom *tmp = _followOnRoots[targetPrevAtom];
+ _followOnRoots[ai] = tmp;
+ continue;
+ }
+ if (!checkAllPrevAtomsZeroSize(targetAtom))
+ break;
+ _followOnNexts[ai] = _followOnRoots[targetAtom];
+ setChainRoot(_followOnRoots[targetAtom], _followOnRoots[ai]);
+ }
+ }
+}
+
+/// Build an ordinal override map by traversing the followon chain, and
+/// assigning ordinals to each atom, if the atoms have their ordinals
+/// already assigned skip the atom and move to the next. This is the
+/// main map thats used to sort the atoms while comparing two atoms together
+void
+LayoutPass::buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range) {
+ ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap");
+ uint64_t index = 0;
+ for (const DefinedAtom *ai : range) {
+ const DefinedAtom *atom = ai;
+ if (_ordinalOverrideMap.find(atom) != _ordinalOverrideMap.end())
+ continue;
+ AtomToAtomT::iterator start = _followOnRoots.find(atom);
+ if (start == _followOnRoots.end())
+ continue;
+ for (const DefinedAtom *nextAtom = start->second; nextAtom;
+ nextAtom = _followOnNexts[nextAtom]) {
+ AtomToOrdinalT::iterator pos = _ordinalOverrideMap.find(nextAtom);
+ if (pos == _ordinalOverrideMap.end())
+ _ordinalOverrideMap[nextAtom] = index++;
+ }
+ }
+}
+
+std::vector<LayoutPass::SortKey>
+LayoutPass::decorate(File::AtomRange<DefinedAtom> &atomRange) const {
+ std::vector<SortKey> ret;
+ for (OwningAtomPtr<DefinedAtom> &atom : atomRange.owning_ptrs()) {
+ auto ri = _followOnRoots.find(atom.get());
+ auto oi = _ordinalOverrideMap.find(atom.get());
+ const auto *root = (ri == _followOnRoots.end()) ? atom.get() : ri->second;
+ uint64_t override = (oi == _ordinalOverrideMap.end()) ? 0 : oi->second;
+ ret.push_back(SortKey(std::move(atom), root, override));
+ }
+ return ret;
+}
+
+void LayoutPass::undecorate(File::AtomRange<DefinedAtom> &atomRange,
+ std::vector<SortKey> &keys) const {
+ size_t i = 0;
+ for (SortKey &k : keys)
+ atomRange[i++] = std::move(k._atom);
+}
+
+/// Perform the actual pass
+llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
+ LLVM_DEBUG(llvm::dbgs() << "******** Laying out atoms:\n");
+ // sort the atoms
+ ScopedTask task(getDefaultDomain(), "LayoutPass");
+ File::AtomRange<DefinedAtom> atomRange = mergedFile.defined();
+
+ // Build follow on tables
+ buildFollowOnTable(atomRange);
+
+ // Check the structure of followon graph if running in debug mode.
+ LLVM_DEBUG(checkFollowonChain(atomRange));
+
+ // Build override maps
+ buildOrdinalOverrideMap(atomRange);
+
+ LLVM_DEBUG({
+ llvm::dbgs() << "unsorted atoms:\n";
+ printDefinedAtoms(atomRange);
+ });
+
+ std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
+ llvm::parallelSort(
+ vec,
+ [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
+ return compareAtoms(l, r, _customSorter);
+ });
+ LLVM_DEBUG(checkTransitivity(vec, _customSorter));
+ undecorate(atomRange, vec);
+
+ LLVM_DEBUG({
+ llvm::dbgs() << "sorted atoms:\n";
+ printDefinedAtoms(atomRange);
+ });
+
+ LLVM_DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n");
+ return llvm::Error::success();
+}
+
+void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ pm.add(std::make_unique<LayoutPass>(
+ ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
+ bool & leftBeforeRight) ->bool {
+ return ctx.customAtomOrderer(left, right, leftBeforeRight);
+ }));
+}
+
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
+#define LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
+
+#include "lld/Core/File.h"
+#include "lld/Core/Pass.h"
+#include "lld/Core/Reader.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/DenseMap.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace lld {
+class DefinedAtom;
+class SimpleFile;
+
+namespace mach_o {
+
+/// This linker pass does the layout of the atoms. The pass is done after the
+/// order their .o files were found on the command line, then by order of the
+/// atoms (address) in the .o file. But some atoms have a preferred location
+/// in their section (such as pinned to the start or end of the section), so
+/// the sort must take that into account too.
+class LayoutPass : public Pass {
+public:
+ struct SortKey {
+ SortKey(OwningAtomPtr<DefinedAtom> &&atom,
+ const DefinedAtom *root, uint64_t override)
+ : _atom(std::move(atom)), _root(root), _override(override) {}
+ OwningAtomPtr<DefinedAtom> _atom;
+ const DefinedAtom *_root;
+ uint64_t _override;
+
+ // Note, these are only here to appease MSVC bots which didn't like
+ // the same methods being implemented/deleted in OwningAtomPtr.
+ SortKey(SortKey &&key) : _atom(std::move(key._atom)), _root(key._root),
+ _override(key._override) {
+ key._root = nullptr;
+ }
+
+ SortKey &operator=(SortKey &&key) {
+ _atom = std::move(key._atom);
+ _root = key._root;
+ key._root = nullptr;
+ _override = key._override;
+ return *this;
+ }
+
+ private:
+ SortKey(const SortKey &) = delete;
+ void operator=(const SortKey&) = delete;
+ };
+
+ typedef std::function<bool (const DefinedAtom *left, const DefinedAtom *right,
+ bool &leftBeforeRight)> SortOverride;
+
+ LayoutPass(const Registry ®istry, SortOverride sorter);
+
+ /// Sorts atoms in mergedFile by content type then by command line order.
+ llvm::Error perform(SimpleFile &mergedFile) override;
+
+ ~LayoutPass() override = default;
+
+private:
+ // Build the followOn atoms chain as specified by the kindLayoutAfter
+ // reference type
+ void buildFollowOnTable(const File::AtomRange<DefinedAtom> &range);
+
+ // Build a map of Atoms to ordinals for sorting the atoms
+ void buildOrdinalOverrideMap(const File::AtomRange<DefinedAtom> &range);
+
+ const Registry &_registry;
+ SortOverride _customSorter;
+
+ typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT;
+ typedef llvm::DenseMap<const DefinedAtom *, uint64_t> AtomToOrdinalT;
+
+ // A map to be used to sort atoms. It represents the order of atoms in the
+ // result; if Atom X is mapped to atom Y in this map, X will be located
+ // immediately before Y in the output file. Y might be mapped to another
+ // atom, constructing a follow-on chain. An atom cannot be mapped to more
+ // than one atom unless all but one atom are of size zero.
+ AtomToAtomT _followOnNexts;
+
+ // A map to be used to sort atoms. It's a map from an atom to its root of
+ // follow-on chain. A root atom is mapped to itself. If an atom is not in
+ // _followOnNexts, the atom is not in this map, and vice versa.
+ AtomToAtomT _followOnRoots;
+
+ AtomToOrdinalT _ordinalOverrideMap;
+
+ // Helper methods for buildFollowOnTable().
+ const DefinedAtom *findAtomFollowedBy(const DefinedAtom *targetAtom);
+ bool checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom);
+
+ void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root);
+
+ std::vector<SortKey> decorate(File::AtomRange<DefinedAtom> &atomRange) const;
+
+ void undecorate(File::AtomRange<DefinedAtom> &atomRange,
+ std::vector<SortKey> &keys) const;
+
+ // Check if the follow-on graph is a correct structure. For debugging only.
+ void checkFollowonChain(const File::AtomRange<DefinedAtom> &range);
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_LAYOUT_PASS_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachOLinkingContext.cpp ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "ArchHandler.h"
+#include "File.h"
+#include "FlatNamespaceFile.h"
+#include "MachONormalizedFile.h"
+#include "MachOPasses.h"
+#include "SectCreateFile.h"
+#include "lld/Common/Driver.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/PassManager.h"
+#include "lld/Core/Reader.h"
+#include "lld/Core/Writer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
+#include <algorithm>
+
+using lld::mach_o::ArchHandler;
+using lld::mach_o::MachOFile;
+using lld::mach_o::MachODylibFile;
+using namespace llvm::MachO;
+
+namespace lld {
+
+bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) {
+ result = 0;
+
+ if (str.empty())
+ return false;
+
+ SmallVector<StringRef, 3> parts;
+ llvm::SplitString(str, parts, ".");
+
+ unsigned long long num;
+ if (llvm::getAsUnsignedInteger(parts[0], 10, num))
+ return true;
+ if (num > 65535)
+ return true;
+ result = num << 16;
+
+ if (parts.size() > 1) {
+ if (llvm::getAsUnsignedInteger(parts[1], 10, num))
+ return true;
+ if (num > 255)
+ return true;
+ result |= (num << 8);
+ }
+
+ if (parts.size() > 2) {
+ if (llvm::getAsUnsignedInteger(parts[2], 10, num))
+ return true;
+ if (num > 255)
+ return true;
+ result |= num;
+ }
+
+ return false;
+}
+
+bool MachOLinkingContext::parsePackedVersion(StringRef str, uint64_t &result) {
+ result = 0;
+
+ if (str.empty())
+ return false;
+
+ SmallVector<StringRef, 5> parts;
+ llvm::SplitString(str, parts, ".");
+
+ unsigned long long num;
+ if (llvm::getAsUnsignedInteger(parts[0], 10, num))
+ return true;
+ if (num > 0xFFFFFF)
+ return true;
+ result = num << 40;
+
+ unsigned Shift = 30;
+ for (StringRef str : llvm::makeArrayRef(parts).slice(1)) {
+ if (llvm::getAsUnsignedInteger(str, 10, num))
+ return true;
+ if (num > 0x3FF)
+ return true;
+ result |= (num << Shift);
+ Shift -= 10;
+ }
+
+ return false;
+}
+
+MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = {
+ { "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
+ { "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL },
+ { "ppc", arch_ppc, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL },
+ { "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 },
+ { "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
+ { "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
+ { "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
+ { "", arch_unknown,false, 0, 0 }
+};
+
+MachOLinkingContext::Arch
+MachOLinkingContext::archFromCpuType(uint32_t cputype, uint32_t cpusubtype) {
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if ((info->cputype == cputype) && (info->cpusubtype == cpusubtype))
+ return info->arch;
+ }
+ return arch_unknown;
+}
+
+MachOLinkingContext::Arch
+MachOLinkingContext::archFromName(StringRef archName) {
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->archName.equals(archName))
+ return info->arch;
+ }
+ return arch_unknown;
+}
+
+StringRef MachOLinkingContext::nameFromArch(Arch arch) {
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch)
+ return info->archName;
+ }
+ return "<unknown>";
+}
+
+uint32_t MachOLinkingContext::cpuTypeFromArch(Arch arch) {
+ assert(arch != arch_unknown);
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch)
+ return info->cputype;
+ }
+ llvm_unreachable("Unknown arch type");
+}
+
+uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) {
+ assert(arch != arch_unknown);
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch)
+ return info->cpusubtype;
+ }
+ llvm_unreachable("Unknown arch type");
+}
+
+bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) {
+ return mach_o::normalized::isThinObjectFile(path, arch);
+}
+
+bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset,
+ uint32_t &size) {
+ return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size);
+}
+
+MachOLinkingContext::MachOLinkingContext() {}
+
+MachOLinkingContext::~MachOLinkingContext() {
+ // Atoms are allocated on BumpPtrAllocator's on File's.
+ // As we transfer atoms from one file to another, we need to clear all of the
+ // atoms before we remove any of the BumpPtrAllocator's.
+ auto &nodes = getNodes();
+ for (unsigned i = 0, e = nodes.size(); i != e; ++i) {
+ FileNode *node = dyn_cast<FileNode>(nodes[i].get());
+ if (!node)
+ continue;
+ File *file = node->getFile();
+ file->clearAtoms();
+ }
+}
+
+void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os,
+ uint32_t minOSVersion,
+ bool exportDynamicSymbols) {
+ _outputMachOType = type;
+ _arch = arch;
+ _os = os;
+ _osMinVersion = minOSVersion;
+
+ // If min OS not specified on command line, use reasonable defaults.
+ // Note that we only do sensible defaults when emitting something other than
+ // object and preload.
+ if (_outputMachOType != llvm::MachO::MH_OBJECT &&
+ _outputMachOType != llvm::MachO::MH_PRELOAD) {
+ if (minOSVersion == 0) {
+ switch (_arch) {
+ case arch_x86_64:
+ case arch_x86:
+ parsePackedVersion("10.8", _osMinVersion);
+ _os = MachOLinkingContext::OS::macOSX;
+ break;
+ case arch_armv6:
+ case arch_armv7:
+ case arch_armv7s:
+ case arch_arm64:
+ parsePackedVersion("7.0", _osMinVersion);
+ _os = MachOLinkingContext::OS::iOS;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ switch (_outputMachOType) {
+ case llvm::MachO::MH_EXECUTE:
+ // If targeting newer OS, use _main
+ if (minOS("10.8", "6.0")) {
+ _entrySymbolName = "_main";
+ } else {
+ // If targeting older OS, use start (in crt1.o)
+ _entrySymbolName = "start";
+ }
+
+ // __PAGEZERO defaults to 4GB on 64-bit (except for PP64 which lld does not
+ // support) and 4KB on 32-bit.
+ if (is64Bit(_arch)) {
+ _pageZeroSize = 0x100000000;
+ } else {
+ _pageZeroSize = 0x1000;
+ }
+
+ // Initial base address is __PAGEZERO size.
+ _baseAddress = _pageZeroSize;
+
+ // Make PIE by default when targetting newer OSs.
+ switch (os) {
+ case OS::macOSX:
+ if (minOSVersion >= 0x000A0700) // MacOSX 10.7
+ _pie = true;
+ break;
+ case OS::iOS:
+ if (minOSVersion >= 0x00040300) // iOS 4.3
+ _pie = true;
+ break;
+ case OS::iOS_simulator:
+ _pie = true;
+ break;
+ case OS::unknown:
+ break;
+ }
+ setGlobalsAreDeadStripRoots(exportDynamicSymbols);
+ break;
+ case llvm::MachO::MH_DYLIB:
+ setGlobalsAreDeadStripRoots(exportDynamicSymbols);
+ break;
+ case llvm::MachO::MH_BUNDLE:
+ break;
+ case llvm::MachO::MH_OBJECT:
+ _printRemainingUndefines = false;
+ _allowRemainingUndefines = true;
+ break;
+ default:
+ break;
+ }
+
+ // Set default segment page sizes based on arch.
+ if (arch == arch_arm64)
+ _pageSize = 4*4096;
+}
+
+uint32_t MachOLinkingContext::getCPUType() const {
+ return cpuTypeFromArch(_arch);
+}
+
+uint32_t MachOLinkingContext::getCPUSubType() const {
+ return cpuSubtypeFromArch(_arch);
+}
+
+bool MachOLinkingContext::is64Bit(Arch arch) {
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch) {
+ return (info->cputype & CPU_ARCH_ABI64);
+ }
+ }
+ // unknown archs are not 64-bit.
+ return false;
+}
+
+bool MachOLinkingContext::isHostEndian(Arch arch) {
+ assert(arch != arch_unknown);
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch) {
+ return (info->littleEndian == llvm::sys::IsLittleEndianHost);
+ }
+ }
+ llvm_unreachable("Unknown arch type");
+}
+
+bool MachOLinkingContext::isBigEndian(Arch arch) {
+ assert(arch != arch_unknown);
+ for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
+ if (info->arch == arch) {
+ return ! info->littleEndian;
+ }
+ }
+ llvm_unreachable("Unknown arch type");
+}
+
+bool MachOLinkingContext::is64Bit() const {
+ return is64Bit(_arch);
+}
+
+bool MachOLinkingContext::outputTypeHasEntry() const {
+ switch (_outputMachOType) {
+ case MH_EXECUTE:
+ case MH_DYLINKER:
+ case MH_PRELOAD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::needsStubsPass() const {
+ switch (_outputMachOType) {
+ case MH_EXECUTE:
+ return !_outputMachOTypeStatic;
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::needsGOTPass() const {
+ // GOT pass not used in -r mode.
+ if (_outputMachOType == MH_OBJECT)
+ return false;
+ // Only some arches use GOT pass.
+ switch (_arch) {
+ case arch_x86_64:
+ case arch_arm64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::needsCompactUnwindPass() const {
+ switch (_outputMachOType) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ return archHandler().needsCompactUnwind();
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::needsObjCPass() const {
+ // ObjC pass is only needed if any of the inputs were ObjC.
+ return _objcConstraint != objc_unknown;
+}
+
+bool MachOLinkingContext::needsShimPass() const {
+ // Shim pass only used in final executables.
+ if (_outputMachOType == MH_OBJECT)
+ return false;
+ // Only 32-bit arm arches use Shim pass.
+ switch (_arch) {
+ case arch_armv6:
+ case arch_armv7:
+ case arch_armv7s:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::needsTLVPass() const {
+ switch (_outputMachOType) {
+ case MH_BUNDLE:
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+StringRef MachOLinkingContext::binderSymbolName() const {
+ return archHandler().stubInfo().binderSymbolName;
+}
+
+bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const {
+ uint32_t parsedVersion;
+ switch (_os) {
+ case OS::macOSX:
+ if (parsePackedVersion(mac, parsedVersion))
+ return false;
+ return _osMinVersion >= parsedVersion;
+ case OS::iOS:
+ case OS::iOS_simulator:
+ if (parsePackedVersion(iOS, parsedVersion))
+ return false;
+ return _osMinVersion >= parsedVersion;
+ case OS::unknown:
+ // If we don't know the target, then assume that we don't meet the min OS.
+ // This matches the ld64 behaviour
+ return false;
+ }
+ llvm_unreachable("invalid OS enum");
+}
+
+bool MachOLinkingContext::addEntryPointLoadCommand() const {
+ if ((_outputMachOType == MH_EXECUTE) && !_outputMachOTypeStatic) {
+ return minOS("10.8", "6.0");
+ }
+ return false;
+}
+
+bool MachOLinkingContext::addUnixThreadLoadCommand() const {
+ switch (_outputMachOType) {
+ case MH_EXECUTE:
+ if (_outputMachOTypeStatic)
+ return true;
+ else
+ return !minOS("10.8", "6.0");
+ break;
+ case MH_DYLINKER:
+ case MH_PRELOAD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOLinkingContext::pathExists(StringRef path) const {
+ if (!_testingFileUsage)
+ return llvm::sys::fs::exists(path.str());
+
+ // Otherwise, we're in test mode: only files explicitly provided on the
+ // command-line exist.
+ std::string key = path.str();
+ std::replace(key.begin(), key.end(), '\\', '/');
+ return _existingPaths.find(key) != _existingPaths.end();
+}
+
+bool MachOLinkingContext::fileExists(StringRef path) const {
+ bool found = pathExists(path);
+ // Log search misses.
+ if (!found)
+ addInputFileNotFound(path);
+
+ // When testing, file is never opened, so logging is done here.
+ if (_testingFileUsage && found)
+ addInputFileDependency(path);
+
+ return found;
+}
+
+void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) {
+ _syslibRoots = paths;
+}
+
+void MachOLinkingContext::addRpath(StringRef rpath) {
+ _rpaths.push_back(rpath);
+}
+
+void MachOLinkingContext::addModifiedSearchDir(StringRef libPath,
+ bool isSystemPath) {
+ bool addedModifiedPath = false;
+
+ // -syslibroot only applies to absolute paths.
+ if (libPath.startswith("/")) {
+ for (auto syslibRoot : _syslibRoots) {
+ SmallString<256> path(syslibRoot);
+ llvm::sys::path::append(path, libPath);
+ if (pathExists(path)) {
+ _searchDirs.push_back(path.str().copy(_allocator));
+ addedModifiedPath = true;
+ }
+ }
+ }
+
+ if (addedModifiedPath)
+ return;
+
+ // Finally, if only one -syslibroot is given, system paths which aren't in it
+ // get suppressed.
+ if (_syslibRoots.size() != 1 || !isSystemPath) {
+ if (pathExists(libPath)) {
+ _searchDirs.push_back(libPath);
+ }
+ }
+}
+
+void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath,
+ bool isSystemPath) {
+ bool pathAdded = false;
+
+ // -syslibroot only used with to absolute framework search paths.
+ if (fwPath.startswith("/")) {
+ for (auto syslibRoot : _syslibRoots) {
+ SmallString<256> path(syslibRoot);
+ llvm::sys::path::append(path, fwPath);
+ if (pathExists(path)) {
+ _frameworkDirs.push_back(path.str().copy(_allocator));
+ pathAdded = true;
+ }
+ }
+ }
+ // If fwPath found in any -syslibroot, then done.
+ if (pathAdded)
+ return;
+
+ // If only one -syslibroot, system paths not in that SDK are suppressed.
+ if (isSystemPath && (_syslibRoots.size() == 1))
+ return;
+
+ // Only use raw fwPath if that directory exists.
+ if (pathExists(fwPath))
+ _frameworkDirs.push_back(fwPath);
+}
+
+llvm::Optional<StringRef>
+MachOLinkingContext::searchDirForLibrary(StringRef path,
+ StringRef libName) const {
+ SmallString<256> fullPath;
+ if (libName.endswith(".o")) {
+ // A request ending in .o is special: just search for the file directly.
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, libName);
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+ return llvm::None;
+ }
+
+ // Search for stub library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
+ // Search for dynamic library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
+ // If not, try for a static library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".a");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
+ return llvm::None;
+}
+
+llvm::Optional<StringRef>
+MachOLinkingContext::searchLibrary(StringRef libName) const {
+ SmallString<256> path;
+ for (StringRef dir : searchDirs()) {
+ llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName);
+ if (searchDir)
+ return searchDir;
+ }
+
+ return llvm::None;
+}
+
+llvm::Optional<StringRef>
+MachOLinkingContext::findPathForFramework(StringRef fwName) const{
+ SmallString<256> fullPath;
+ for (StringRef dir : frameworkDirs()) {
+ fullPath.assign(dir);
+ llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName);
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+ }
+
+ return llvm::None;
+}
+
+bool MachOLinkingContext::validateImpl() {
+ // TODO: if -arch not specified, look at arch of first .o file.
+
+ if (_currentVersion && _outputMachOType != MH_DYLIB) {
+ error("-current_version can only be used with dylibs");
+ return false;
+ }
+
+ if (_compatibilityVersion && _outputMachOType != MH_DYLIB) {
+ error("-compatibility_version can only be used with dylibs");
+ return false;
+ }
+
+ if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) {
+ error("-mark_dead_strippable_dylib can only be used with dylibs");
+ return false;
+ }
+
+ if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) {
+ error("-bundle_loader can only be used with Mach-O bundles");
+ return false;
+ }
+
+ // If -exported_symbols_list used, all exported symbols must be defined.
+ if (_exportMode == ExportMode::exported) {
+ for (const auto &symbol : _exportedSymbols)
+ addInitialUndefinedSymbol(symbol.getKey());
+ }
+
+ // If -dead_strip, set up initial live symbols.
+ if (deadStrip()) {
+ // Entry point is live.
+ if (outputTypeHasEntry())
+ addDeadStripRoot(entrySymbolName());
+ // Lazy binding helper is live.
+ if (needsStubsPass())
+ addDeadStripRoot(binderSymbolName());
+ // If using -exported_symbols_list, make all exported symbols live.
+ if (_exportMode == ExportMode::exported) {
+ setGlobalsAreDeadStripRoots(false);
+ for (const auto &symbol : _exportedSymbols)
+ addDeadStripRoot(symbol.getKey());
+ }
+ }
+
+ addOutputFileDependency(outputPath());
+
+ return true;
+}
+
+void MachOLinkingContext::addPasses(PassManager &pm) {
+ // objc pass should be before layout pass. Otherwise test cases may contain
+ // no atoms which confuses the layout pass.
+ if (needsObjCPass())
+ mach_o::addObjCPass(pm, *this);
+ mach_o::addLayoutPass(pm, *this);
+ if (needsStubsPass())
+ mach_o::addStubsPass(pm, *this);
+ if (needsCompactUnwindPass())
+ mach_o::addCompactUnwindPass(pm, *this);
+ if (needsGOTPass())
+ mach_o::addGOTPass(pm, *this);
+ if (needsTLVPass())
+ mach_o::addTLVPass(pm, *this);
+ if (needsShimPass())
+ mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass.
+}
+
+Writer &MachOLinkingContext::writer() const {
+ if (!_writer)
+ _writer = createWriterMachO(*this);
+ return *_writer;
+}
+
+ErrorOr<std::unique_ptr<MemoryBuffer>>
+MachOLinkingContext::getMemoryBuffer(StringRef path) {
+ addInputFileDependency(path);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
+ MemoryBuffer::getFileOrSTDIN(path);
+ if (std::error_code ec = mbOrErr.getError())
+ return ec;
+ std::unique_ptr<MemoryBuffer> mb = std::move(mbOrErr.get());
+
+ // If buffer contains a fat file, find required arch in fat buffer
+ // and switch buffer to point to just that required slice.
+ uint32_t offset;
+ uint32_t size;
+ if (sliceFromFatFile(mb->getMemBufferRef(), offset, size))
+ return MemoryBuffer::getFileSlice(path, size, offset);
+ return std::move(mb);
+}
+
+MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = getMemoryBuffer(path);
+ if (mbOrErr.getError())
+ return nullptr;
+
+ ErrorOr<std::unique_ptr<File>> fileOrErr =
+ registry().loadFile(std::move(mbOrErr.get()));
+ if (!fileOrErr)
+ return nullptr;
+ std::unique_ptr<File> &file = fileOrErr.get();
+ file->parse();
+ MachODylibFile *result = reinterpret_cast<MachODylibFile *>(file.get());
+ // Node object now owned by _indirectDylibs vector.
+ _indirectDylibs.push_back(std::move(file));
+ return result;
+}
+
+MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) {
+ // See if already loaded.
+ auto pos = _pathToDylibMap.find(path);
+ if (pos != _pathToDylibMap.end())
+ return pos->second;
+
+ // Search -L paths if of the form "libXXX.dylib"
+ std::pair<StringRef, StringRef> split = path.rsplit('/');
+ StringRef leafName = split.second;
+ if (leafName.startswith("lib") && leafName.endswith(".dylib")) {
+ // FIXME: Need to enhance searchLibrary() to only look for .dylib
+ auto libPath = searchLibrary(leafName);
+ if (libPath)
+ return loadIndirectDylib(libPath.getValue());
+ }
+
+ // Try full path with sysroot.
+ for (StringRef sysPath : _syslibRoots) {
+ SmallString<256> fullPath;
+ fullPath.assign(sysPath);
+ llvm::sys::path::append(fullPath, path);
+ if (pathExists(fullPath))
+ return loadIndirectDylib(fullPath);
+ }
+
+ // Try full path.
+ if (pathExists(path)) {
+ return loadIndirectDylib(path);
+ }
+
+ return nullptr;
+}
+
+uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const {
+ auto pos = _pathToDylibMap.find(installName);
+ if (pos != _pathToDylibMap.end())
+ return pos->second->currentVersion();
+ else
+ return 0x10000; // 1.0
+}
+
+uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const {
+ auto pos = _pathToDylibMap.find(installName);
+ if (pos != _pathToDylibMap.end())
+ return pos->second->compatVersion();
+ else
+ return 0x10000; // 1.0
+}
+
+void MachOLinkingContext::createImplicitFiles(
+ std::vector<std::unique_ptr<File> > &result) {
+ // Add indirect dylibs by asking each linked dylib to add its indirects.
+ // Iterate until no more dylibs get loaded.
+ size_t dylibCount = 0;
+ while (dylibCount != _allDylibs.size()) {
+ dylibCount = _allDylibs.size();
+ for (MachODylibFile *dylib : _allDylibs) {
+ dylib->loadReExportedDylibs([this] (StringRef path) -> MachODylibFile* {
+ return findIndirectDylib(path); });
+ }
+ }
+
+ // Let writer add output type specific extras.
+ writer().createImplicitFiles(result);
+
+ // If undefinedMode is != error, add a FlatNamespaceFile instance. This will
+ // provide a SharedLibraryAtom for symbols that aren't defined elsewhere.
+ if (undefinedMode() != UndefinedMode::error) {
+ result.emplace_back(new mach_o::FlatNamespaceFile(*this));
+ _flatNamespaceFile = result.back().get();
+ }
+}
+
+void MachOLinkingContext::registerDylib(MachODylibFile *dylib,
+ bool upward) const {
+ std::lock_guard<std::mutex> lock(_dylibsMutex);
+
+ if (!llvm::count(_allDylibs, dylib))
+ _allDylibs.push_back(dylib);
+ _pathToDylibMap[dylib->installName()] = dylib;
+ // If path is different than install name, register path too.
+ if (!dylib->path().equals(dylib->installName()))
+ _pathToDylibMap[dylib->path()] = dylib;
+ if (upward)
+ _upwardDylibs.insert(dylib);
+}
+
+bool MachOLinkingContext::isUpwardDylib(StringRef installName) const {
+ for (MachODylibFile *dylib : _upwardDylibs) {
+ if (dylib->installName().equals(installName))
+ return true;
+ }
+ return false;
+}
+
+ArchHandler &MachOLinkingContext::archHandler() const {
+ if (!_archHandler)
+ _archHandler = ArchHandler::create(_arch);
+ return *_archHandler;
+}
+
+void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect,
+ uint16_t align) {
+ SectionAlign entry = { seg, sect, align };
+ _sectAligns.push_back(entry);
+}
+
+void MachOLinkingContext::addSectCreateSection(
+ StringRef seg, StringRef sect,
+ std::unique_ptr<MemoryBuffer> content) {
+
+ if (!_sectCreateFile) {
+ auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>();
+ _sectCreateFile = sectCreateFile.get();
+ getNodes().push_back(std::make_unique<FileNode>(std::move(sectCreateFile)));
+ }
+
+ assert(_sectCreateFile && "sectcreate file does not exist.");
+ _sectCreateFile->addSection(seg, sect, std::move(content));
+}
+
+bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect,
+ uint16_t &align) const {
+ for (const SectionAlign &entry : _sectAligns) {
+ if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) {
+ align = entry.align;
+ return true;
+ }
+ }
+ return false;
+}
+
+void MachOLinkingContext::addExportSymbol(StringRef sym) {
+ // Support old crufty export lists with bogus entries.
+ if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) {
+ llvm::errs() << "warning: ignoring " << sym << " in export list\n";
+ return;
+ }
+ // Only i386 MacOSX uses old ABI, so don't change those.
+ if ((_os != OS::macOSX) || (_arch != arch_x86)) {
+ // ObjC has two different ABIs. Be nice and allow one export list work for
+ // both ABIs by renaming symbols.
+ if (sym.startswith(".objc_class_name_")) {
+ std::string abi2className("_OBJC_CLASS_$_");
+ abi2className += sym.substr(17);
+ _exportedSymbols.insert(copy(abi2className));
+ std::string abi2metaclassName("_OBJC_METACLASS_$_");
+ abi2metaclassName += sym.substr(17);
+ _exportedSymbols.insert(copy(abi2metaclassName));
+ return;
+ }
+ }
+
+ // FIXME: Support wildcards.
+ _exportedSymbols.insert(sym);
+}
+
+bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const {
+ switch (_exportMode) {
+ case ExportMode::globals:
+ llvm_unreachable("exportSymbolNamed() should not be called in this mode");
+ break;
+ case ExportMode::exported:
+ return _exportedSymbols.count(sym);
+ case ExportMode::unexported:
+ return !_exportedSymbols.count(sym);
+ }
+ llvm_unreachable("_exportMode unknown enum value");
+}
+
+std::string MachOLinkingContext::demangle(StringRef symbolName) const {
+ // Only try to demangle symbols if -demangle on command line
+ if (!demangleSymbols())
+ return std::string(symbolName);
+
+ // Only try to demangle symbols that look like C++ symbols
+ if (!symbolName.startswith("__Z"))
+ return std::string(symbolName);
+
+ SmallString<256> symBuff;
+ StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
+ // Mach-O has extra leading underscore that needs to be removed.
+ const char *cstr = nullTermSym.data() + 1;
+ int status;
+ char *demangled = llvm::itaniumDemangle(cstr, nullptr, nullptr, &status);
+ if (demangled) {
+ std::string result(demangled);
+ // __cxa_demangle() always uses a malloc'ed buffer to return the result.
+ free(demangled);
+ return result;
+ }
+
+ return std::string(symbolName);
+}
+
+static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
+ char Opcode, StringRef Path) {
+ if (!DepInfo)
+ return;
+
+ *DepInfo << Opcode;
+ *DepInfo << Path;
+ *DepInfo << '\0';
+}
+
+std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
+ std::error_code ec;
+ _dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
+ new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
+ if (ec) {
+ _dependencyInfo.reset();
+ return ec;
+ }
+
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x00, "lld" /*FIXME*/);
+ return std::error_code();
+}
+
+void MachOLinkingContext::addInputFileDependency(StringRef path) const {
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x10, path);
+}
+
+void MachOLinkingContext::addInputFileNotFound(StringRef path) const {
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x11, path);
+}
+
+void MachOLinkingContext::addOutputFileDependency(StringRef path) const {
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x40, path);
+}
+
+void MachOLinkingContext::appendOrderedSymbol(StringRef symbol,
+ StringRef filename) {
+ // To support sorting static functions which may have the same name in
+ // multiple .o files, _orderFiles maps the symbol name to a vector
+ // of OrderFileNode each of which can specify a file prefix.
+ OrderFileNode info;
+ if (!filename.empty())
+ info.fileFilter = copy(filename);
+ info.order = _orderFileEntries++;
+ _orderFiles[symbol].push_back(info);
+}
+
+bool
+MachOLinkingContext::findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
+ const DefinedAtom *atom,
+ unsigned &ordinal) {
+ const File *objFile = &atom->file();
+ assert(objFile);
+ StringRef objName = objFile->path();
+ std::pair<StringRef, StringRef> dirAndLeaf = objName.rsplit('/');
+ if (!dirAndLeaf.second.empty())
+ objName = dirAndLeaf.second;
+ for (const OrderFileNode &info : nodes) {
+ if (info.fileFilter.empty()) {
+ // Have unprefixed symbol name in order file that matches this atom.
+ ordinal = info.order;
+ return true;
+ }
+ if (info.fileFilter.equals(objName)) {
+ // Have prefixed symbol name in order file that matches atom's path.
+ ordinal = info.order;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left,
+ const DefinedAtom *right,
+ bool &leftBeforeRight) const {
+ // No custom sorting if no order file entries.
+ if (!_orderFileEntries)
+ return false;
+
+ // Order files can only order named atoms.
+ StringRef leftName = left->name();
+ StringRef rightName = right->name();
+ if (leftName.empty() || rightName.empty())
+ return false;
+
+ // If neither is in order file list, no custom sorter.
+ auto leftPos = _orderFiles.find(leftName);
+ auto rightPos = _orderFiles.find(rightName);
+ bool leftIsOrdered = (leftPos != _orderFiles.end());
+ bool rightIsOrdered = (rightPos != _orderFiles.end());
+ if (!leftIsOrdered && !rightIsOrdered)
+ return false;
+
+ // There could be multiple symbols with same name but different file prefixes.
+ unsigned leftOrder;
+ unsigned rightOrder;
+ bool foundLeft =
+ leftIsOrdered && findOrderOrdinal(leftPos->getValue(), left, leftOrder);
+ bool foundRight = rightIsOrdered &&
+ findOrderOrdinal(rightPos->getValue(), right, rightOrder);
+ if (!foundLeft && !foundRight)
+ return false;
+
+ // If only one is in order file list, ordered one goes first.
+ if (foundLeft != foundRight)
+ leftBeforeRight = foundLeft;
+ else
+ leftBeforeRight = (leftOrder < rightOrder);
+
+ return true;
+}
+
+static bool isLibrary(const std::unique_ptr<Node> &elem) {
+ if (FileNode *node = dyn_cast<FileNode>(const_cast<Node *>(elem.get()))) {
+ File *file = node->getFile();
+ return isa<SharedLibraryFile>(file) || isa<ArchiveLibraryFile>(file);
+ }
+ return false;
+}
+
+// The darwin linker processes input files in two phases. The first phase
+// links in all object (.o) files in command line order. The second phase
+// links in libraries in command line order.
+// In this function we reorder the input files so that all the object files
+// comes before any library file. We also make a group for the library files
+// so that the Resolver will reiterate over the libraries as long as we find
+// new undefines from libraries.
+void MachOLinkingContext::finalizeInputFiles() {
+ std::vector<std::unique_ptr<Node>> &elements = getNodes();
+ llvm::stable_sort(elements, [](const std::unique_ptr<Node> &a,
+ const std::unique_ptr<Node> &b) {
+ return !isLibrary(a) && isLibrary(b);
+ });
+ size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
+ elements.push_back(std::make_unique<GroupEnd>(numLibs));
+}
+
+llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
+ auto *machoFile = dyn_cast<MachOFile>(&file);
+ if (!machoFile)
+ return llvm::Error::success();
+
+ // Check that the arch of the context matches that of the file.
+ // Also set the arch of the context if it didn't have one.
+ if (_arch == arch_unknown) {
+ _arch = machoFile->arch();
+ } else if (machoFile->arch() != arch_unknown && machoFile->arch() != _arch) {
+ // Archs are different.
+ return llvm::make_error<GenericError>(file.path() +
+ Twine(" cannot be linked due to incompatible architecture"));
+ }
+
+ // Check that the OS of the context matches that of the file.
+ // Also set the OS of the context if it didn't have one.
+ if (_os == OS::unknown) {
+ _os = machoFile->OS();
+ } else if (machoFile->OS() != OS::unknown && machoFile->OS() != _os) {
+ // OSes are different.
+ return llvm::make_error<GenericError>(file.path() +
+ Twine(" cannot be linked due to incompatible operating systems"));
+ }
+
+ // Check that if the objc info exists, that it is compatible with the target
+ // OS.
+ switch (machoFile->objcConstraint()) {
+ case objc_unknown:
+ // The file is not compiled with objc, so skip the checks.
+ break;
+ case objc_gc_only:
+ case objc_supports_gc:
+ llvm_unreachable("GC support should already have thrown an error");
+ case objc_retainReleaseForSimulator:
+ // The file is built with simulator objc, so make sure that the context
+ // is also building with simulator support.
+ if (_os != OS::iOS_simulator)
+ return llvm::make_error<GenericError>(file.path() +
+ Twine(" cannot be linked. It contains ObjC built for the simulator"
+ " while we are linking a non-simulator target"));
+ assert((_objcConstraint == objc_unknown ||
+ _objcConstraint == objc_retainReleaseForSimulator) &&
+ "Must be linking with retain/release for the simulator");
+ _objcConstraint = objc_retainReleaseForSimulator;
+ break;
+ case objc_retainRelease:
+ // The file is built without simulator objc, so make sure that the
+ // context is also building without simulator support.
+ if (_os == OS::iOS_simulator)
+ return llvm::make_error<GenericError>(file.path() +
+ Twine(" cannot be linked. It contains ObjC built for a non-simulator"
+ " target while we are linking a simulator target"));
+ assert((_objcConstraint == objc_unknown ||
+ _objcConstraint == objc_retainRelease) &&
+ "Must be linking with retain/release for a non-simulator target");
+ _objcConstraint = objc_retainRelease;
+ break;
+ }
+
+ // Check that the swift version of the context matches that of the file.
+ // Also set the swift version of the context if it didn't have one.
+ if (!_swiftVersion) {
+ _swiftVersion = machoFile->swiftVersion();
+ } else if (machoFile->swiftVersion() &&
+ machoFile->swiftVersion() != _swiftVersion) {
+ // Swift versions are different.
+ return llvm::make_error<GenericError>("different swift versions");
+ }
+
+ return llvm::Error::success();
+}
+
+} // end namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFile.h -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file These data structures comprise the "normalized" view of
+/// mach-o object files. The normalized view is an in-memory only data structure
+/// which is always in native endianness and pointer size.
+///
+/// The normalized view easily converts to and from YAML using YAML I/O.
+///
+/// The normalized view converts to and from binary mach-o object files using
+/// the writeBinary() and readBinary() functions.
+///
+/// The normalized view converts to and from lld::Atoms using the
+/// normalizedToAtoms() and normalizedFromAtoms().
+///
+/// Overall, the conversion paths available look like:
+///
+/// +---------------+
+/// | binary mach-o |
+/// +---------------+
+/// ^
+/// |
+/// v
+/// +------------+ +------+
+/// | normalized | <-> | yaml |
+/// +------------+ +------+
+/// ^
+/// |
+/// v
+/// +-------+
+/// | Atoms |
+/// +-------+
+///
+
+#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
+#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
+
+#include "DebugInfo.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using llvm::BumpPtrAllocator;
+using llvm::yaml::Hex64;
+using llvm::yaml::Hex32;
+using llvm::yaml::Hex16;
+using llvm::yaml::Hex8;
+using llvm::yaml::SequenceTraits;
+using llvm::MachO::HeaderFileType;
+using llvm::MachO::BindType;
+using llvm::MachO::RebaseType;
+using llvm::MachO::NListType;
+using llvm::MachO::RelocationInfoType;
+using llvm::MachO::SectionType;
+using llvm::MachO::LoadCommandType;
+using llvm::MachO::ExportSymbolKind;
+using llvm::MachO::DataRegionType;
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+
+/// The real mach-o relocation record is 8-bytes on disk and is
+/// encoded in one of two different bit-field patterns. This
+/// normalized form has the union of all possible fields.
+struct Relocation {
+ Relocation() : offset(0), scattered(false),
+ type(llvm::MachO::GENERIC_RELOC_VANILLA),
+ length(0), pcRel(false), isExtern(false), value(0),
+ symbol(0) { }
+
+ Hex32 offset;
+ bool scattered;
+ RelocationInfoType type;
+ uint8_t length;
+ bool pcRel;
+ bool isExtern;
+ Hex32 value;
+ uint32_t symbol;
+};
+
+/// A typedef so that YAML I/O can treat this vector as a sequence.
+typedef std::vector<Relocation> Relocations;
+
+/// A typedef so that YAML I/O can process the raw bytes in a section.
+typedef std::vector<Hex8> ContentBytes;
+
+/// A typedef so that YAML I/O can treat indirect symbols as a flow sequence.
+typedef std::vector<uint32_t> IndirectSymbols;
+
+/// A typedef so that YAML I/O can encode/decode section attributes.
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, SectionAttr)
+
+/// A typedef so that YAML I/O can encode/decode section alignment.
+LLVM_YAML_STRONG_TYPEDEF(uint16_t, SectionAlignment)
+
+/// Mach-O has a 32-bit and 64-bit section record. This normalized form
+/// can support either kind.
+struct Section {
+ Section() : type(llvm::MachO::S_REGULAR),
+ attributes(0), alignment(1), address(0) { }
+
+ StringRef segmentName;
+ StringRef sectionName;
+ SectionType type;
+ SectionAttr attributes;
+ SectionAlignment alignment;
+ Hex64 address;
+ ArrayRef<uint8_t> content;
+ Relocations relocations;
+ IndirectSymbols indirectSymbols;
+};
+
+
+/// A typedef so that YAML I/O can encode/decode the scope bits of an nlist.
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, SymbolScope)
+
+/// A typedef so that YAML I/O can encode/decode the desc bits of an nlist.
+LLVM_YAML_STRONG_TYPEDEF(uint16_t, SymbolDesc)
+
+/// Mach-O has a 32-bit and 64-bit symbol table entry (nlist), and the symbol
+/// type and scope and mixed in the same n_type field. This normalized form
+/// works for any pointer size and separates out the type and scope.
+struct Symbol {
+ Symbol() : type(llvm::MachO::N_UNDF), scope(0), sect(0), desc(0), value(0) { }
+
+ StringRef name;
+ NListType type;
+ SymbolScope scope;
+ uint8_t sect;
+ SymbolDesc desc;
+ Hex64 value;
+};
+
+/// Check whether the given section type indicates a zero-filled section.
+// FIXME: Utility functions of this kind should probably be moved into
+// llvm/Support.
+inline bool isZeroFillSection(SectionType T) {
+ return (T == llvm::MachO::S_ZEROFILL ||
+ T == llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
+}
+
+/// A typedef so that YAML I/O can (de/en)code the protection bits of a segment.
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, VMProtect)
+
+/// A typedef to hold verions X.Y.X packed into 32-bit xxxx.yy.zz
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, PackedVersion)
+
+/// Segments are only used in normalized final linked images (not in relocatable
+/// object files). They specify how a range of the file is loaded.
+struct Segment {
+ StringRef name;
+ Hex64 address;
+ Hex64 size;
+ VMProtect init_access;
+ VMProtect max_access;
+};
+
+/// Only used in normalized final linked images to specify on which dylibs
+/// it depends.
+struct DependentDylib {
+ StringRef path;
+ LoadCommandType kind;
+ PackedVersion compatVersion;
+ PackedVersion currentVersion;
+};
+
+/// A normalized rebasing entry. Only used in normalized final linked images.
+struct RebaseLocation {
+ Hex32 segOffset;
+ uint8_t segIndex;
+ RebaseType kind;
+};
+
+/// A normalized binding entry. Only used in normalized final linked images.
+struct BindLocation {
+ Hex32 segOffset;
+ uint8_t segIndex;
+ BindType kind;
+ bool canBeNull;
+ int ordinal;
+ StringRef symbolName;
+ Hex64 addend;
+};
+
+/// A typedef so that YAML I/O can encode/decode export flags.
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, ExportFlags)
+
+/// A normalized export entry. Only used in normalized final linked images.
+struct Export {
+ StringRef name;
+ Hex64 offset;
+ ExportSymbolKind kind;
+ ExportFlags flags;
+ Hex32 otherOffset;
+ StringRef otherName;
+};
+
+/// A normalized data-in-code entry.
+struct DataInCode {
+ Hex32 offset;
+ Hex16 length;
+ DataRegionType kind;
+};
+
+/// A typedef so that YAML I/O can encode/decode mach_header.flags.
+LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags)
+
+///
+struct NormalizedFile {
+ MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
+ HeaderFileType fileType = llvm::MachO::MH_OBJECT;
+ FileFlags flags = 0;
+ std::vector<Segment> segments; // Not used in object files.
+ std::vector<Section> sections;
+
+ // Symbols sorted by kind.
+ std::vector<Symbol> localSymbols;
+ std::vector<Symbol> globalSymbols;
+ std::vector<Symbol> undefinedSymbols;
+ std::vector<Symbol> stabsSymbols;
+
+ // Maps to load commands with no LINKEDIT content (final linked images only).
+ std::vector<DependentDylib> dependentDylibs;
+ StringRef installName; // dylibs only
+ PackedVersion compatVersion = 0; // dylibs only
+ PackedVersion currentVersion = 0; // dylibs only
+ bool hasUUID = false;
+ bool hasMinVersionLoadCommand = false;
+ bool generateDataInCodeLoadCommand = false;
+ std::vector<StringRef> rpaths;
+ Hex64 entryAddress = 0;
+ Hex64 stackSize = 0;
+ MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown;
+ Hex64 sourceVersion = 0;
+ PackedVersion minOSverson = 0;
+ PackedVersion sdkVersion = 0;
+ LoadCommandType minOSVersionKind = (LoadCommandType)0;
+
+ // Maps to load commands with LINKEDIT content (final linked images only).
+ Hex32 pageSize = 0;
+ std::vector<RebaseLocation> rebasingInfo;
+ std::vector<BindLocation> bindingInfo;
+ std::vector<BindLocation> weakBindingInfo;
+ std::vector<BindLocation> lazyBindingInfo;
+ std::vector<Export> exportInfo;
+ std::vector<uint8_t> functionStarts;
+ std::vector<DataInCode> dataInCode;
+
+ // TODO:
+ // code-signature
+ // split-seg-info
+ // function-starts
+
+ // For any allocations in this struct which need to be owned by this struct.
+ BumpPtrAllocator ownedAllocations;
+};
+
+/// Tests if a file is a non-fat mach-o object file.
+bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch);
+
+/// If the buffer is a fat file with the request arch, then this function
+/// returns true with 'offset' and 'size' set to location of the arch slice
+/// within the buffer. Otherwise returns false;
+bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
+ uint32_t &offset, uint32_t &size);
+
+/// Reads a mach-o file and produces an in-memory normalized view.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+readBinary(std::unique_ptr<MemoryBuffer> &mb,
+ const MachOLinkingContext::Arch arch);
+
+/// Takes in-memory normalized view and writes a mach-o object file.
+llvm::Error writeBinary(const NormalizedFile &file, StringRef path);
+
+size_t headerAndLoadCommandsSize(const NormalizedFile &file,
+ bool includeFunctionStarts);
+
+
+/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+readYaml(std::unique_ptr<MemoryBuffer> &mb);
+
+/// Writes a yaml encoded mach-o files given an in-memory normalized view.
+std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out);
+
+llvm::Error
+normalizedObjectToAtoms(MachOFile *file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs);
+
+llvm::Error
+normalizedDylibToAtoms(MachODylibFile *file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs);
+
+/// Takes in-memory normalized dylib or object and parses it into lld::File
+llvm::Expected<std::unique_ptr<lld::File>>
+normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
+ bool copyRefs);
+
+/// Takes atoms and generates a normalized macho-o view.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &ctxt);
+
+
+} // namespace normalized
+
+/// Class for interfacing mach-o yaml files into generic yaml parsing
+class MachOYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
+public:
+ MachOYamlIOTaggedDocumentHandler(MachOLinkingContext::Arch arch)
+ : _arch(arch) { }
+ bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override;
+private:
+ const MachOLinkingContext::Arch _arch;
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file For mach-o object files, this implementation converts from
+/// mach-o on-disk binary format to in-memory normalized mach-o.
+///
+/// +---------------+
+/// | binary mach-o |
+/// +---------------+
+/// |
+/// |
+/// v
+/// +------------+
+/// | normalized |
+/// +------------+
+
+#include "ArchHandler.h"
+#include "MachONormalizedFile.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <functional>
+#include <system_error>
+
+using namespace llvm::MachO;
+using llvm::object::ExportEntry;
+using llvm::file_magic;
+using llvm::object::MachOObjectFile;
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+// Utility to call a lambda expression on each load command.
+static llvm::Error forEachLoadCommand(
+ StringRef lcRange, unsigned lcCount, bool isBig, bool is64,
+ std::function<bool(uint32_t cmd, uint32_t size, const char *lc)> func) {
+ const char* p = lcRange.begin();
+ for (unsigned i=0; i < lcCount; ++i) {
+ const load_command *lc = reinterpret_cast<const load_command*>(p);
+ load_command lcCopy;
+ const load_command *slc = lc;
+ if (isBig != llvm::sys::IsBigEndianHost) {
+ memcpy(&lcCopy, lc, sizeof(load_command));
+ swapStruct(lcCopy);
+ slc = &lcCopy;
+ }
+ if ( (p + slc->cmdsize) > lcRange.end() )
+ return llvm::make_error<GenericError>("Load command exceeds range");
+
+ if (func(slc->cmd, slc->cmdsize, p))
+ return llvm::Error::success();
+
+ p += slc->cmdsize;
+ }
+
+ return llvm::Error::success();
+}
+
+static std::error_code appendRelocations(Relocations &relocs, StringRef buffer,
+ bool bigEndian,
+ uint32_t reloff, uint32_t nreloc) {
+ if ((reloff + nreloc*8) > buffer.size())
+ return make_error_code(llvm::errc::executable_format_error);
+ const any_relocation_info* relocsArray =
+ reinterpret_cast<const any_relocation_info*>(buffer.begin()+reloff);
+
+ for(uint32_t i=0; i < nreloc; ++i) {
+ relocs.push_back(unpackRelocation(relocsArray[i], bigEndian));
+ }
+ return std::error_code();
+}
+
+static std::error_code
+appendIndirectSymbols(IndirectSymbols &isyms, StringRef buffer, bool isBig,
+ uint32_t istOffset, uint32_t istCount,
+ uint32_t startIndex, uint32_t count) {
+ if ((istOffset + istCount*4) > buffer.size())
+ return make_error_code(llvm::errc::executable_format_error);
+ if (startIndex+count > istCount)
+ return make_error_code(llvm::errc::executable_format_error);
+ const uint8_t *indirectSymbolArray = (const uint8_t *)buffer.data();
+
+ for(uint32_t i=0; i < count; ++i) {
+ isyms.push_back(read32(
+ indirectSymbolArray + (startIndex + i) * sizeof(uint32_t), isBig));
+ }
+ return std::error_code();
+}
+
+
+template <typename T> static T readBigEndian(T t) {
+ if (llvm::sys::IsLittleEndianHost)
+ llvm::sys::swapByteOrder(t);
+ return t;
+}
+
+
+static bool isMachOHeader(const mach_header *mh, bool &is64, bool &isBig) {
+ switch (read32(&mh->magic, false)) {
+ case llvm::MachO::MH_MAGIC:
+ is64 = false;
+ isBig = false;
+ return true;
+ case llvm::MachO::MH_MAGIC_64:
+ is64 = true;
+ isBig = false;
+ return true;
+ case llvm::MachO::MH_CIGAM:
+ is64 = false;
+ isBig = true;
+ return true;
+ case llvm::MachO::MH_CIGAM_64:
+ is64 = true;
+ isBig = true;
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) {
+ // Try opening and mapping file at path.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path);
+ if (b.getError())
+ return false;
+
+ // If file length < 32 it is too small to be mach-o object file.
+ StringRef fileBuffer = b->get()->getBuffer();
+ if (fileBuffer.size() < 32)
+ return false;
+
+ // If file buffer does not start with MH_MAGIC (and variants), not obj file.
+ const mach_header *mh = reinterpret_cast<const mach_header *>(
+ fileBuffer.begin());
+ bool is64, isBig;
+ if (!isMachOHeader(mh, is64, isBig))
+ return false;
+
+ // If not MH_OBJECT, not object file.
+ if (read32(&mh->filetype, isBig) != MH_OBJECT)
+ return false;
+
+ // Lookup up arch from cpu/subtype pair.
+ arch = MachOLinkingContext::archFromCpuType(
+ read32(&mh->cputype, isBig),
+ read32(&mh->cpusubtype, isBig));
+ return true;
+}
+
+bool sliceFromFatFile(MemoryBufferRef mb, MachOLinkingContext::Arch arch,
+ uint32_t &offset, uint32_t &size) {
+ const char *start = mb.getBufferStart();
+ const llvm::MachO::fat_header *fh =
+ reinterpret_cast<const llvm::MachO::fat_header *>(start);
+ if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC)
+ return false;
+ uint32_t nfat_arch = readBigEndian(fh->nfat_arch);
+ const fat_arch *fstart =
+ reinterpret_cast<const fat_arch *>(start + sizeof(fat_header));
+ const fat_arch *fend =
+ reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) +
+ sizeof(fat_arch) * nfat_arch);
+ const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch);
+ const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch);
+ for (const fat_arch *fa = fstart; fa < fend; ++fa) {
+ if ((readBigEndian(fa->cputype) == reqCpuType) &&
+ (readBigEndian(fa->cpusubtype) == reqCpuSubtype)) {
+ offset = readBigEndian(fa->offset);
+ size = readBigEndian(fa->size);
+ if ((offset + size) > mb.getBufferSize())
+ return false;
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Reads a mach-o file and produces an in-memory normalized view.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+readBinary(std::unique_ptr<MemoryBuffer> &mb,
+ const MachOLinkingContext::Arch arch) {
+ // Make empty NormalizedFile.
+ std::unique_ptr<NormalizedFile> f(new NormalizedFile());
+
+ const char *start = mb->getBufferStart();
+ size_t objSize = mb->getBufferSize();
+ const mach_header *mh = reinterpret_cast<const mach_header *>(start);
+
+ uint32_t sliceOffset;
+ uint32_t sliceSize;
+ if (sliceFromFatFile(mb->getMemBufferRef(), arch, sliceOffset, sliceSize)) {
+ start = &start[sliceOffset];
+ objSize = sliceSize;
+ mh = reinterpret_cast<const mach_header *>(start);
+ }
+
+ // Determine endianness and pointer size for mach-o file.
+ bool is64, isBig;
+ if (!isMachOHeader(mh, is64, isBig))
+ return llvm::make_error<GenericError>("File is not a mach-o");
+
+ // Endian swap header, if needed.
+ mach_header headerCopy;
+ const mach_header *smh = mh;
+ if (isBig != llvm::sys::IsBigEndianHost) {
+ memcpy(&headerCopy, mh, sizeof(mach_header));
+ swapStruct(headerCopy);
+ smh = &headerCopy;
+ }
+
+ // Validate head and load commands fit in buffer.
+ const uint32_t lcCount = smh->ncmds;
+ const char *lcStart =
+ start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header));
+ StringRef lcRange(lcStart, smh->sizeofcmds);
+ if (lcRange.end() > (start + objSize))
+ return llvm::make_error<GenericError>("Load commands exceed file size");
+
+ // Get architecture from mach_header.
+ f->arch = MachOLinkingContext::archFromCpuType(smh->cputype, smh->cpusubtype);
+ if (f->arch != arch) {
+ return llvm::make_error<GenericError>(
+ Twine("file is wrong architecture. Expected "
+ "(" + MachOLinkingContext::nameFromArch(arch)
+ + ") found ("
+ + MachOLinkingContext::nameFromArch(f->arch)
+ + ")" ));
+ }
+ // Copy file type and flags
+ f->fileType = HeaderFileType(smh->filetype);
+ f->flags = smh->flags;
+
+
+ // Pre-scan load commands looking for indirect symbol table.
+ uint32_t indirectSymbolTableOffset = 0;
+ uint32_t indirectSymbolTableCount = 0;
+ auto ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
+ [&](uint32_t cmd, uint32_t size,
+ const char *lc) -> bool {
+ if (cmd == LC_DYSYMTAB) {
+ const dysymtab_command *d = reinterpret_cast<const dysymtab_command*>(lc);
+ indirectSymbolTableOffset = read32(&d->indirectsymoff, isBig);
+ indirectSymbolTableCount = read32(&d->nindirectsyms, isBig);
+ return true;
+ }
+ return false;
+ });
+ if (ec)
+ return std::move(ec);
+
+ // Walk load commands looking for segments/sections and the symbol table.
+ const data_in_code_entry *dataInCode = nullptr;
+ const dyld_info_command *dyldInfo = nullptr;
+ uint32_t dataInCodeSize = 0;
+ ec = forEachLoadCommand(lcRange, lcCount, isBig, is64,
+ [&] (uint32_t cmd, uint32_t size, const char* lc) -> bool {
+ switch(cmd) {
+ case LC_SEGMENT_64:
+ if (is64) {
+ const segment_command_64 *seg =
+ reinterpret_cast<const segment_command_64*>(lc);
+ const unsigned sectionCount = read32(&seg->nsects, isBig);
+ const section_64 *sects = reinterpret_cast<const section_64*>
+ (lc + sizeof(segment_command_64));
+ const unsigned lcSize = sizeof(segment_command_64)
+ + sectionCount*sizeof(section_64);
+ // Verify sections don't extend beyond end of segment load command.
+ if (lcSize > size)
+ return true;
+ for (unsigned i=0; i < sectionCount; ++i) {
+ const section_64 *sect = §s[i];
+ Section section;
+ section.segmentName = getString16(sect->segname);
+ section.sectionName = getString16(sect->sectname);
+ section.type = (SectionType)(read32(§->flags, isBig) &
+ SECTION_TYPE);
+ section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES;
+ section.alignment = 1 << read32(§->align, isBig);
+ section.address = read64(§->addr, isBig);
+ const uint8_t *content =
+ (const uint8_t *)start + read32(§->offset, isBig);
+ size_t contentSize = read64(§->size, isBig);
+ // Note: this assign() is copying the content bytes. Ideally,
+ // we can use a custom allocator for vector to avoid the copy.
+ section.content = llvm::makeArrayRef(content, contentSize);
+ appendRelocations(section.relocations, mb->getBuffer(), isBig,
+ read32(§->reloff, isBig),
+ read32(§->nreloc, isBig));
+ if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
+ appendIndirectSymbols(section.indirectSymbols, mb->getBuffer(),
+ isBig,
+ indirectSymbolTableOffset,
+ indirectSymbolTableCount,
+ read32(§->reserved1, isBig),
+ contentSize/4);
+ }
+ f->sections.push_back(section);
+ }
+ }
+ break;
+ case LC_SEGMENT:
+ if (!is64) {
+ const segment_command *seg =
+ reinterpret_cast<const segment_command*>(lc);
+ const unsigned sectionCount = read32(&seg->nsects, isBig);
+ const section *sects = reinterpret_cast<const section*>
+ (lc + sizeof(segment_command));
+ const unsigned lcSize = sizeof(segment_command)
+ + sectionCount*sizeof(section);
+ // Verify sections don't extend beyond end of segment load command.
+ if (lcSize > size)
+ return true;
+ for (unsigned i=0; i < sectionCount; ++i) {
+ const section *sect = §s[i];
+ Section section;
+ section.segmentName = getString16(sect->segname);
+ section.sectionName = getString16(sect->sectname);
+ section.type = (SectionType)(read32(§->flags, isBig) &
+ SECTION_TYPE);
+ section.attributes =
+ read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES;
+ section.alignment = 1 << read32(§->align, isBig);
+ section.address = read32(§->addr, isBig);
+ const uint8_t *content =
+ (const uint8_t *)start + read32(§->offset, isBig);
+ size_t contentSize = read32(§->size, isBig);
+ // Note: this assign() is copying the content bytes. Ideally,
+ // we can use a custom allocator for vector to avoid the copy.
+ section.content = llvm::makeArrayRef(content, contentSize);
+ appendRelocations(section.relocations, mb->getBuffer(), isBig,
+ read32(§->reloff, isBig),
+ read32(§->nreloc, isBig));
+ if (section.type == S_NON_LAZY_SYMBOL_POINTERS) {
+ appendIndirectSymbols(
+ section.indirectSymbols, mb->getBuffer(), isBig,
+ indirectSymbolTableOffset, indirectSymbolTableCount,
+ read32(§->reserved1, isBig), contentSize / 4);
+ }
+ f->sections.push_back(section);
+ }
+ }
+ break;
+ case LC_SYMTAB: {
+ const symtab_command *st = reinterpret_cast<const symtab_command*>(lc);
+ const char *strings = start + read32(&st->stroff, isBig);
+ const uint32_t strSize = read32(&st->strsize, isBig);
+ // Validate string pool and symbol table all in buffer.
+ if (read32((const uint8_t *)&st->stroff, isBig) +
+ read32((const uint8_t *)&st->strsize, isBig) >
+ objSize)
+ return true;
+ if (is64) {
+ const uint32_t symOffset = read32(&st->symoff, isBig);
+ const uint32_t symCount = read32(&st->nsyms, isBig);
+ if ( symOffset+(symCount*sizeof(nlist_64)) > objSize)
+ return true;
+ const nlist_64 *symbols =
+ reinterpret_cast<const nlist_64 *>(start + symOffset);
+ // Convert each nlist_64 to a lld::mach_o::normalized::Symbol.
+ for(uint32_t i=0; i < symCount; ++i) {
+ nlist_64 tempSym;
+ memcpy(&tempSym, &symbols[i], sizeof(nlist_64));
+ const nlist_64 *sin = &tempSym;
+ if (isBig != llvm::sys::IsBigEndianHost)
+ swapStruct(tempSym);
+ Symbol sout;
+ if (sin->n_strx > strSize)
+ return true;
+ sout.name = &strings[sin->n_strx];
+ sout.type = static_cast<NListType>(sin->n_type & (N_STAB|N_TYPE));
+ sout.scope = (sin->n_type & (N_PEXT|N_EXT));
+ sout.sect = sin->n_sect;
+ sout.desc = sin->n_desc;
+ sout.value = sin->n_value;
+ if (sin->n_type & N_STAB)
+ f->stabsSymbols.push_back(sout);
+ else if (sout.type == N_UNDF)
+ f->undefinedSymbols.push_back(sout);
+ else if (sin->n_type & N_EXT)
+ f->globalSymbols.push_back(sout);
+ else
+ f->localSymbols.push_back(sout);
+ }
+ } else {
+ const uint32_t symOffset = read32(&st->symoff, isBig);
+ const uint32_t symCount = read32(&st->nsyms, isBig);
+ if ( symOffset+(symCount*sizeof(nlist)) > objSize)
+ return true;
+ const nlist *symbols =
+ reinterpret_cast<const nlist *>(start + symOffset);
+ // Convert each nlist to a lld::mach_o::normalized::Symbol.
+ for(uint32_t i=0; i < symCount; ++i) {
+ const nlist *sin = &symbols[i];
+ nlist tempSym;
+ if (isBig != llvm::sys::IsBigEndianHost) {
+ tempSym = *sin; swapStruct(tempSym); sin = &tempSym;
+ }
+ Symbol sout;
+ if (sin->n_strx > strSize)
+ return true;
+ sout.name = &strings[sin->n_strx];
+ sout.type = (NListType)(sin->n_type & N_TYPE);
+ sout.scope = (sin->n_type & (N_PEXT|N_EXT));
+ sout.sect = sin->n_sect;
+ sout.desc = sin->n_desc;
+ sout.value = sin->n_value;
+ if (sout.type == N_UNDF)
+ f->undefinedSymbols.push_back(sout);
+ else if (sout.scope == (SymbolScope)N_EXT)
+ f->globalSymbols.push_back(sout);
+ else if (sin->n_type & N_STAB)
+ f->stabsSymbols.push_back(sout);
+ else
+ f->localSymbols.push_back(sout);
+ }
+ }
+ }
+ break;
+ case LC_ID_DYLIB: {
+ const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
+ f->installName = lc + read32(&dl->dylib.name, isBig);
+ f->currentVersion = read32(&dl->dylib.current_version, isBig);
+ f->compatVersion = read32(&dl->dylib.compatibility_version, isBig);
+ }
+ break;
+ case LC_DATA_IN_CODE: {
+ const linkedit_data_command *ldc =
+ reinterpret_cast<const linkedit_data_command*>(lc);
+ dataInCode = reinterpret_cast<const data_in_code_entry *>(
+ start + read32(&ldc->dataoff, isBig));
+ dataInCodeSize = read32(&ldc->datasize, isBig);
+ }
+ break;
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB: {
+ const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc);
+ DependentDylib entry;
+ entry.path = lc + read32(&dl->dylib.name, isBig);
+ entry.kind = LoadCommandType(cmd);
+ entry.compatVersion = read32(&dl->dylib.compatibility_version, isBig);
+ entry.currentVersion = read32(&dl->dylib.current_version, isBig);
+ f->dependentDylibs.push_back(entry);
+ }
+ break;
+ case LC_RPATH: {
+ const rpath_command *rpc = reinterpret_cast<const rpath_command *>(lc);
+ f->rpaths.push_back(lc + read32(&rpc->path, isBig));
+ }
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ dyldInfo = reinterpret_cast<const dyld_info_command*>(lc);
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_WATCHOS:
+ case LC_VERSION_MIN_TVOS:
+ // If we are emitting an object file, then we may take the load command
+ // kind from these commands and pass it on to the output
+ // file.
+ f->minOSVersionKind = (LoadCommandType)cmd;
+ break;
+ }
+ return false;
+ });
+ if (ec)
+ return std::move(ec);
+
+ if (dataInCode) {
+ // Convert on-disk data_in_code_entry array to DataInCode vector.
+ for (unsigned i=0; i < dataInCodeSize/sizeof(data_in_code_entry); ++i) {
+ DataInCode entry;
+ entry.offset = read32(&dataInCode[i].offset, isBig);
+ entry.length = read16(&dataInCode[i].length, isBig);
+ entry.kind =
+ (DataRegionType)read16((const uint8_t *)&dataInCode[i].kind, isBig);
+ f->dataInCode.push_back(entry);
+ }
+ }
+
+ if (dyldInfo) {
+ // If any exports, extract and add to normalized exportInfo vector.
+ if (dyldInfo->export_size) {
+ const uint8_t *trieStart = reinterpret_cast<const uint8_t *>(
+ start + read32(&dyldInfo->export_off, isBig));
+ ArrayRef<uint8_t> trie(trieStart, read32(&dyldInfo->export_size, isBig));
+ Error Err = Error::success();
+ for (const ExportEntry &trieExport : MachOObjectFile::exports(Err, trie)) {
+ Export normExport;
+ normExport.name = trieExport.name().copy(f->ownedAllocations);
+ normExport.offset = trieExport.address();
+ normExport.kind = ExportSymbolKind(trieExport.flags() & EXPORT_SYMBOL_FLAGS_KIND_MASK);
+ normExport.flags = trieExport.flags() & ~EXPORT_SYMBOL_FLAGS_KIND_MASK;
+ normExport.otherOffset = trieExport.other();
+ if (!trieExport.otherName().empty())
+ normExport.otherName = trieExport.otherName().copy(f->ownedAllocations);
+ f->exportInfo.push_back(normExport);
+ }
+ if (Err)
+ return std::move(Err);
+ }
+ }
+
+ return std::move(f);
+}
+
+class MachOObjectReader : public Reader {
+public:
+ MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ return (magic == file_magic::macho_object && mb.getBufferSize() > 32);
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®istry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<MachOFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
+class MachODylibReader : public Reader {
+public:
+ MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ switch (magic) {
+ case file_magic::macho_dynamically_linked_shared_lib:
+ case file_magic::macho_dynamically_linked_shared_lib_stub:
+ return mb.getBufferSize() > 32;
+ default:
+ return false;
+ }
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®istry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<MachODylibFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
+class MachOTAPIReader : public Reader {
+public:
+ MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ return magic == file_magic::tapi_file;
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®istry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<TAPIFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
+} // namespace normalized
+} // namespace mach_o
+
+void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) {
+ MachOLinkingContext::Arch arch = ctx.arch();
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
+ addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
+ ctx.archHandler().kindStrings());
+ add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
+ new mach_o::MachOYamlIOTaggedDocumentHandler(arch)));
+}
+
+
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
+#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
+
+#include "MachONormalizedFile.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/LEB128.h"
+#include <system_error>
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+class ByteBuffer {
+public:
+ ByteBuffer() : _ostream(_bytes) { }
+
+ void append_byte(uint8_t b) {
+ _ostream << b;
+ }
+ void append_uleb128(uint64_t value) {
+ llvm::encodeULEB128(value, _ostream);
+ }
+ void append_uleb128Fixed(uint64_t value, unsigned byteCount) {
+ unsigned min = llvm::getULEB128Size(value);
+ assert(min <= byteCount);
+ unsigned pad = byteCount - min;
+ llvm::encodeULEB128(value, _ostream, pad);
+ }
+ void append_sleb128(int64_t value) {
+ llvm::encodeSLEB128(value, _ostream);
+ }
+ void append_string(StringRef str) {
+ _ostream << str;
+ append_byte(0);
+ }
+ void align(unsigned alignment) {
+ while ( (_ostream.tell() % alignment) != 0 )
+ append_byte(0);
+ }
+ size_t size() {
+ return _ostream.tell();
+ }
+ const uint8_t *bytes() {
+ return reinterpret_cast<const uint8_t*>(_ostream.str().data());
+ }
+
+private:
+ SmallVector<char, 128> _bytes;
+ // Stream ivar must be after SmallVector ivar to construct properly.
+ llvm::raw_svector_ostream _ostream;
+};
+
+using namespace llvm::support::endian;
+using llvm::sys::getSwappedBytes;
+
+template<typename T>
+static inline uint16_t read16(const T *loc, bool isBig) {
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
+ return isBig ? read16be(loc) : read16le(loc);
+}
+
+template<typename T>
+static inline uint32_t read32(const T *loc, bool isBig) {
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
+ return isBig ? read32be(loc) : read32le(loc);
+}
+
+template<typename T>
+static inline uint64_t read64(const T *loc, bool isBig) {
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
+ return isBig ? read64be(loc) : read64le(loc);
+}
+
+inline void write16(uint8_t *loc, uint16_t value, bool isBig) {
+ if (isBig)
+ write16be(loc, value);
+ else
+ write16le(loc, value);
+}
+
+inline void write32(uint8_t *loc, uint32_t value, bool isBig) {
+ if (isBig)
+ write32be(loc, value);
+ else
+ write32le(loc, value);
+}
+
+inline void write64(uint8_t *loc, uint64_t value, bool isBig) {
+ if (isBig)
+ write64be(loc, value);
+ else
+ write64le(loc, value);
+}
+
+inline uint32_t
+bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit,
+ uint8_t bitCount) {
+ const uint32_t mask = ((1<<bitCount)-1);
+ const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
+ return (value >> shift) & mask;
+}
+
+inline void
+bitFieldSet(uint32_t &bits, bool isBigEndianBigField, uint32_t newBits,
+ uint8_t firstBit, uint8_t bitCount) {
+ const uint32_t mask = ((1<<bitCount)-1);
+ assert((newBits & mask) == newBits);
+ const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
+ bits &= ~(mask << shift);
+ bits |= (newBits << shift);
+}
+
+inline Relocation unpackRelocation(const llvm::MachO::any_relocation_info &r,
+ bool isBigEndian) {
+ uint32_t r0 = read32(&r.r_word0, isBigEndian);
+ uint32_t r1 = read32(&r.r_word1, isBigEndian);
+
+ Relocation result;
+ if (r0 & llvm::MachO::R_SCATTERED) {
+ // scattered relocation record always laid out like big endian bit field
+ result.offset = bitFieldExtract(r0, true, 8, 24);
+ result.scattered = true;
+ result.type = (RelocationInfoType)
+ bitFieldExtract(r0, true, 4, 4);
+ result.length = bitFieldExtract(r0, true, 2, 2);
+ result.pcRel = bitFieldExtract(r0, true, 1, 1);
+ result.isExtern = false;
+ result.value = r1;
+ result.symbol = 0;
+ } else {
+ result.offset = r0;
+ result.scattered = false;
+ result.type = (RelocationInfoType)
+ bitFieldExtract(r1, isBigEndian, 28, 4);
+ result.length = bitFieldExtract(r1, isBigEndian, 25, 2);
+ result.pcRel = bitFieldExtract(r1, isBigEndian, 24, 1);
+ result.isExtern = bitFieldExtract(r1, isBigEndian, 27, 1);
+ result.value = 0;
+ result.symbol = bitFieldExtract(r1, isBigEndian, 0, 24);
+ }
+ return result;
+}
+
+
+inline llvm::MachO::any_relocation_info
+packRelocation(const Relocation &r, bool swap, bool isBigEndian) {
+ uint32_t r0 = 0;
+ uint32_t r1 = 0;
+
+ if (r.scattered) {
+ r1 = r.value;
+ bitFieldSet(r0, true, r.offset, 8, 24);
+ bitFieldSet(r0, true, r.type, 4, 4);
+ bitFieldSet(r0, true, r.length, 2, 2);
+ bitFieldSet(r0, true, r.pcRel, 1, 1);
+ bitFieldSet(r0, true, r.scattered, 0, 1); // R_SCATTERED
+ } else {
+ r0 = r.offset;
+ bitFieldSet(r1, isBigEndian, r.type, 28, 4);
+ bitFieldSet(r1, isBigEndian, r.isExtern, 27, 1);
+ bitFieldSet(r1, isBigEndian, r.length, 25, 2);
+ bitFieldSet(r1, isBigEndian, r.pcRel, 24, 1);
+ bitFieldSet(r1, isBigEndian, r.symbol, 0, 24);
+ }
+
+ llvm::MachO::any_relocation_info result;
+ result.r_word0 = swap ? getSwappedBytes(r0) : r0;
+ result.r_word1 = swap ? getSwappedBytes(r1) : r1;
+ return result;
+}
+
+inline StringRef getString16(const char s[16]) {
+ // The StringRef(const char *) constructor passes the const char * to
+ // strlen(), so we can't use this constructor here, because if there is no
+ // null terminator in s, then strlen() will read past the end of the array.
+ return StringRef(s, strnlen(s, 16));
+}
+
+inline void setString16(StringRef str, char s[16]) {
+ memset(s, 0, 16);
+ memcpy(s, str.begin(), (str.size() > 16) ? 16: str.size());
+}
+
+// Implemented in normalizedToAtoms() and used by normalizedFromAtoms() so
+// that the same table can be used to map mach-o sections to and from
+// DefinedAtom::ContentType.
+void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
+ StringRef &segmentName,
+ StringRef §ionName,
+ SectionType §ionType,
+ SectionAttr §ionAttrs,
+ bool &relocsToDefinedCanBeImplicit);
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file For mach-o object files, this implementation converts normalized
+/// mach-o in memory to mach-o binary on disk.
+///
+/// +---------------+
+/// | binary mach-o |
+/// +---------------+
+/// ^
+/// |
+/// |
+/// +------------+
+/// | normalized |
+/// +------------+
+
+#include "MachONormalizedFile.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/ilist.h"
+#include "llvm/ADT/ilist_node.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <functional>
+#include <list>
+#include <map>
+#include <system_error>
+
+using namespace llvm::MachO;
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+struct TrieNode; // Forward declaration.
+
+struct TrieEdge : public llvm::ilist_node<TrieEdge> {
+ TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {}
+
+ StringRef _subString;
+ struct TrieNode *_child;
+};
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
+
+
+namespace llvm {
+using lld::mach_o::normalized::TrieEdge;
+template <>
+struct ilist_alloc_traits<TrieEdge> : ilist_noalloc_traits<TrieEdge> {};
+} // namespace llvm
+
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+struct TrieNode {
+ typedef llvm::ilist<TrieEdge> TrieEdgeList;
+
+ TrieNode(StringRef s)
+ : _cummulativeString(s), _address(0), _flags(0), _other(0),
+ _trieOffset(0), _hasExportInfo(false) {}
+ ~TrieNode() = default;
+
+ void addSymbol(const Export &entry, BumpPtrAllocator &allocator,
+ std::vector<TrieNode *> &allNodes);
+
+ void addOrderedNodes(const Export &entry,
+ std::vector<TrieNode *> &allNodes);
+ bool updateOffset(uint32_t &offset);
+ void appendToByteBuffer(ByteBuffer &out);
+
+private:
+ StringRef _cummulativeString;
+ TrieEdgeList _children;
+ uint64_t _address;
+ uint64_t _flags;
+ uint64_t _other;
+ StringRef _importedName;
+ uint32_t _trieOffset;
+ bool _hasExportInfo;
+ bool _ordered = false;
+};
+
+/// Utility class for writing a mach-o binary file given an in-memory
+/// normalized file.
+class MachOFileLayout {
+public:
+ /// All layout computation is done in the constructor.
+ MachOFileLayout(const NormalizedFile &file, bool alwaysIncludeFunctionStarts);
+
+ /// Returns the final file size as computed in the constructor.
+ size_t size() const;
+
+ // Returns size of the mach_header and load commands.
+ size_t headerAndLoadCommandsSize() const;
+
+ /// Writes the normalized file as a binary mach-o file to the specified
+ /// path. This does not have a stream interface because the generated
+ /// file may need the 'x' bit set.
+ llvm::Error writeBinary(StringRef path);
+
+private:
+ uint32_t loadCommandsSize(uint32_t &count,
+ bool alwaysIncludeFunctionStarts);
+ void buildFileOffsets();
+ void writeMachHeader();
+ llvm::Error writeLoadCommands();
+ void writeSectionContent();
+ void writeRelocations();
+ void writeSymbolTable();
+ void writeRebaseInfo();
+ void writeBindingInfo();
+ void writeLazyBindingInfo();
+ void writeExportInfo();
+ void writeFunctionStartsInfo();
+ void writeDataInCodeInfo();
+ void writeLinkEditContent();
+ void buildLinkEditInfo();
+ void buildRebaseInfo();
+ void buildBindInfo();
+ void buildLazyBindInfo();
+ void buildExportTrie();
+ void computeFunctionStartsSize();
+ void computeDataInCodeSize();
+ void computeSymbolTableSizes();
+ void buildSectionRelocations();
+ void appendSymbols(const std::vector<Symbol> &symbols,
+ uint32_t &symOffset, uint32_t &strOffset);
+ uint32_t indirectSymbolIndex(const Section §, uint32_t &index);
+ uint32_t indirectSymbolElementSize(const Section §);
+
+ // For use as template parameter to load command methods.
+ struct MachO64Trait {
+ typedef llvm::MachO::segment_command_64 command;
+ typedef llvm::MachO::section_64 section;
+ enum { LC = llvm::MachO::LC_SEGMENT_64 };
+ };
+
+ // For use as template parameter to load command methods.
+ struct MachO32Trait {
+ typedef llvm::MachO::segment_command command;
+ typedef llvm::MachO::section section;
+ enum { LC = llvm::MachO::LC_SEGMENT };
+ };
+
+ template <typename T>
+ llvm::Error writeSingleSegmentLoadCommand(uint8_t *&lc);
+ template <typename T> llvm::Error writeSegmentLoadCommands(uint8_t *&lc);
+
+ uint32_t pointerAlign(uint32_t value);
+ static StringRef dyldPath();
+
+ struct SegExtraInfo {
+ uint32_t fileOffset;
+ uint32_t fileSize;
+ std::vector<const Section*> sections;
+ };
+ typedef std::map<const Segment*, SegExtraInfo> SegMap;
+ struct SectionExtraInfo {
+ uint32_t fileOffset;
+ };
+ typedef std::map<const Section*, SectionExtraInfo> SectionMap;
+
+ const NormalizedFile &_file;
+ std::error_code _ec;
+ uint8_t *_buffer;
+ const bool _is64;
+ const bool _swap;
+ const bool _bigEndianArch;
+ uint64_t _seg1addr;
+ uint32_t _startOfLoadCommands;
+ uint32_t _countOfLoadCommands;
+ uint32_t _endOfLoadCommands;
+ uint32_t _startOfRelocations;
+ uint32_t _startOfFunctionStarts;
+ uint32_t _startOfDataInCode;
+ uint32_t _startOfSymbols;
+ uint32_t _startOfIndirectSymbols;
+ uint32_t _startOfSymbolStrings;
+ uint32_t _endOfSymbolStrings;
+ uint32_t _symbolTableLocalsStartIndex;
+ uint32_t _symbolTableGlobalsStartIndex;
+ uint32_t _symbolTableUndefinesStartIndex;
+ uint32_t _symbolStringPoolSize;
+ uint32_t _symbolTableSize;
+ uint32_t _functionStartsSize;
+ uint32_t _dataInCodeSize;
+ uint32_t _indirectSymbolTableCount;
+ // Used in object file creation only
+ uint32_t _startOfSectionsContent;
+ uint32_t _endOfSectionsContent;
+ // Used in final linked image only
+ uint32_t _startOfLinkEdit;
+ uint32_t _startOfRebaseInfo;
+ uint32_t _endOfRebaseInfo;
+ uint32_t _startOfBindingInfo;
+ uint32_t _endOfBindingInfo;
+ uint32_t _startOfLazyBindingInfo;
+ uint32_t _endOfLazyBindingInfo;
+ uint32_t _startOfExportTrie;
+ uint32_t _endOfExportTrie;
+ uint32_t _endOfLinkEdit;
+ uint64_t _addressOfLinkEdit;
+ SegMap _segInfo;
+ SectionMap _sectInfo;
+ ByteBuffer _rebaseInfo;
+ ByteBuffer _bindingInfo;
+ ByteBuffer _lazyBindingInfo;
+ ByteBuffer _weakBindingInfo;
+ ByteBuffer _exportTrie;
+};
+
+size_t headerAndLoadCommandsSize(const NormalizedFile &file,
+ bool includeFunctionStarts) {
+ MachOFileLayout layout(file, includeFunctionStarts);
+ return layout.headerAndLoadCommandsSize();
+}
+
+StringRef MachOFileLayout::dyldPath() {
+ return "/usr/lib/dyld";
+}
+
+uint32_t MachOFileLayout::pointerAlign(uint32_t value) {
+ return llvm::alignTo(value, _is64 ? 8 : 4);
+}
+
+
+size_t MachOFileLayout::headerAndLoadCommandsSize() const {
+ return _endOfLoadCommands;
+}
+
+MachOFileLayout::MachOFileLayout(const NormalizedFile &file,
+ bool alwaysIncludeFunctionStarts)
+ : _file(file),
+ _is64(MachOLinkingContext::is64Bit(file.arch)),
+ _swap(!MachOLinkingContext::isHostEndian(file.arch)),
+ _bigEndianArch(MachOLinkingContext::isBigEndian(file.arch)),
+ _seg1addr(INT64_MAX) {
+ _startOfLoadCommands = _is64 ? sizeof(mach_header_64) : sizeof(mach_header);
+ const size_t segCommandBaseSize =
+ (_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
+ const size_t sectsSize = (_is64 ? sizeof(section_64) : sizeof(section));
+ if (file.fileType == llvm::MachO::MH_OBJECT) {
+ // object files have just one segment load command containing all sections
+ _endOfLoadCommands = _startOfLoadCommands
+ + segCommandBaseSize
+ + file.sections.size() * sectsSize
+ + sizeof(symtab_command);
+ _countOfLoadCommands = 2;
+ if (file.hasMinVersionLoadCommand) {
+ _endOfLoadCommands += sizeof(version_min_command);
+ _countOfLoadCommands++;
+ }
+ if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
+ _endOfLoadCommands += sizeof(linkedit_data_command);
+ _countOfLoadCommands++;
+ }
+ if (_file.generateDataInCodeLoadCommand) {
+ _endOfLoadCommands += sizeof(linkedit_data_command);
+ _countOfLoadCommands++;
+ }
+ // Assign file offsets to each section.
+ _startOfSectionsContent = _endOfLoadCommands;
+ unsigned relocCount = 0;
+ uint64_t offset = _startOfSectionsContent;
+ for (const Section § : file.sections) {
+ if (isZeroFillSection(sect.type))
+ _sectInfo[§].fileOffset = 0;
+ else {
+ offset = llvm::alignTo(offset, sect.alignment);
+ _sectInfo[§].fileOffset = offset;
+ offset += sect.content.size();
+ }
+ relocCount += sect.relocations.size();
+ }
+ _endOfSectionsContent = offset;
+
+ computeSymbolTableSizes();
+ computeFunctionStartsSize();
+ computeDataInCodeSize();
+
+ // Align start of relocations.
+ _startOfRelocations = pointerAlign(_endOfSectionsContent);
+ _startOfFunctionStarts = _startOfRelocations + relocCount * 8;
+ _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
+ _startOfSymbols = _startOfDataInCode + _dataInCodeSize;
+ // Add Indirect symbol table.
+ _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
+ // Align start of symbol table and symbol strings.
+ _startOfSymbolStrings = _startOfIndirectSymbols
+ + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
+ _endOfSymbolStrings = _startOfSymbolStrings
+ + pointerAlign(_symbolStringPoolSize);
+ _endOfLinkEdit = _endOfSymbolStrings;
+ DEBUG_WITH_TYPE("MachOFileLayout",
+ llvm::dbgs() << "MachOFileLayout()\n"
+ << " startOfLoadCommands=" << _startOfLoadCommands << "\n"
+ << " countOfLoadCommands=" << _countOfLoadCommands << "\n"
+ << " endOfLoadCommands=" << _endOfLoadCommands << "\n"
+ << " startOfRelocations=" << _startOfRelocations << "\n"
+ << " startOfSymbols=" << _startOfSymbols << "\n"
+ << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
+ << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
+ << " startOfSectionsContent=" << _startOfSectionsContent << "\n"
+ << " endOfSectionsContent=" << _endOfSectionsContent << "\n");
+ } else {
+ // Final linked images have one load command per segment.
+ _endOfLoadCommands = _startOfLoadCommands
+ + loadCommandsSize(_countOfLoadCommands,
+ alwaysIncludeFunctionStarts);
+
+ // Assign section file offsets.
+ buildFileOffsets();
+ buildLinkEditInfo();
+
+ // LINKEDIT of final linked images has in order:
+ // rebase info, binding info, lazy binding info, weak binding info,
+ // data-in-code, symbol table, indirect symbol table, symbol table strings.
+ _startOfRebaseInfo = _startOfLinkEdit;
+ _endOfRebaseInfo = _startOfRebaseInfo + _rebaseInfo.size();
+ _startOfBindingInfo = _endOfRebaseInfo;
+ _endOfBindingInfo = _startOfBindingInfo + _bindingInfo.size();
+ _startOfLazyBindingInfo = _endOfBindingInfo;
+ _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size();
+ _startOfExportTrie = _endOfLazyBindingInfo;
+ _endOfExportTrie = _startOfExportTrie + _exportTrie.size();
+ _startOfFunctionStarts = _endOfExportTrie;
+ _startOfDataInCode = _startOfFunctionStarts + _functionStartsSize;
+ _startOfSymbols = _startOfDataInCode + _dataInCodeSize;
+ _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize;
+ _startOfSymbolStrings = _startOfIndirectSymbols
+ + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t));
+ _endOfSymbolStrings = _startOfSymbolStrings
+ + pointerAlign(_symbolStringPoolSize);
+ _endOfLinkEdit = _endOfSymbolStrings;
+ DEBUG_WITH_TYPE("MachOFileLayout",
+ llvm::dbgs() << "MachOFileLayout()\n"
+ << " startOfLoadCommands=" << _startOfLoadCommands << "\n"
+ << " countOfLoadCommands=" << _countOfLoadCommands << "\n"
+ << " endOfLoadCommands=" << _endOfLoadCommands << "\n"
+ << " startOfLinkEdit=" << _startOfLinkEdit << "\n"
+ << " startOfRebaseInfo=" << _startOfRebaseInfo << "\n"
+ << " endOfRebaseInfo=" << _endOfRebaseInfo << "\n"
+ << " startOfBindingInfo=" << _startOfBindingInfo << "\n"
+ << " endOfBindingInfo=" << _endOfBindingInfo << "\n"
+ << " startOfLazyBindingInfo=" << _startOfLazyBindingInfo << "\n"
+ << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n"
+ << " startOfExportTrie=" << _startOfExportTrie << "\n"
+ << " endOfExportTrie=" << _endOfExportTrie << "\n"
+ << " startOfFunctionStarts=" << _startOfFunctionStarts << "\n"
+ << " startOfDataInCode=" << _startOfDataInCode << "\n"
+ << " startOfSymbols=" << _startOfSymbols << "\n"
+ << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n"
+ << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n"
+ << " addressOfLinkEdit=" << _addressOfLinkEdit << "\n");
+ }
+}
+
+uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count,
+ bool alwaysIncludeFunctionStarts) {
+ uint32_t size = 0;
+ count = 0;
+
+ const size_t segCommandSize =
+ (_is64 ? sizeof(segment_command_64) : sizeof(segment_command));
+ const size_t sectionSize = (_is64 ? sizeof(section_64) : sizeof(section));
+
+ // Add LC_SEGMENT for each segment.
+ size += _file.segments.size() * segCommandSize;
+ count += _file.segments.size();
+ // Add section record for each section.
+ size += _file.sections.size() * sectionSize;
+
+ // If creating a dylib, add LC_ID_DYLIB.
+ if (_file.fileType == llvm::MachO::MH_DYLIB) {
+ size += sizeof(dylib_command) + pointerAlign(_file.installName.size() + 1);
+ ++count;
+ }
+
+ // Add LC_DYLD_INFO
+ size += sizeof(dyld_info_command);
+ ++count;
+
+ // Add LC_SYMTAB
+ size += sizeof(symtab_command);
+ ++count;
+
+ // Add LC_DYSYMTAB
+ if (_file.fileType != llvm::MachO::MH_PRELOAD) {
+ size += sizeof(dysymtab_command);
+ ++count;
+ }
+
+ // If main executable add LC_LOAD_DYLINKER
+ if (_file.fileType == llvm::MachO::MH_EXECUTE) {
+ size += pointerAlign(sizeof(dylinker_command) + dyldPath().size()+1);
+ ++count;
+ }
+
+ // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS,
+ // LC_VERSION_MIN_TVOS
+ if (_file.hasMinVersionLoadCommand) {
+ size += sizeof(version_min_command);
+ ++count;
+ }
+
+ // Add LC_SOURCE_VERSION
+ size += sizeof(source_version_command);
+ ++count;
+
+ // If main executable add LC_MAIN
+ if (_file.fileType == llvm::MachO::MH_EXECUTE) {
+ size += sizeof(entry_point_command);
+ ++count;
+ }
+
+ // Add LC_LOAD_DYLIB for each dependent dylib.
+ for (const DependentDylib &dep : _file.dependentDylibs) {
+ size += sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
+ ++count;
+ }
+
+ // Add LC_RPATH
+ for (const StringRef &path : _file.rpaths) {
+ size += pointerAlign(sizeof(rpath_command) + path.size() + 1);
+ ++count;
+ }
+
+ // Add LC_FUNCTION_STARTS if needed
+ if (!_file.functionStarts.empty() || alwaysIncludeFunctionStarts) {
+ size += sizeof(linkedit_data_command);
+ ++count;
+ }
+
+ // Add LC_DATA_IN_CODE if requested. Note, we do encode zero length entries.
+ // FIXME: Zero length entries is only to match ld64. Should we change this?
+ if (_file.generateDataInCodeLoadCommand) {
+ size += sizeof(linkedit_data_command);
+ ++count;
+ }
+
+ return size;
+}
+
+static bool overlaps(const Segment &s1, const Segment &s2) {
+ if (s2.address >= s1.address+s1.size)
+ return false;
+ if (s1.address >= s2.address+s2.size)
+ return false;
+ return true;
+}
+
+static bool overlaps(const Section &s1, const Section &s2) {
+ if (s2.address >= s1.address+s1.content.size())
+ return false;
+ if (s1.address >= s2.address+s2.content.size())
+ return false;
+ return true;
+}
+
+void MachOFileLayout::buildFileOffsets() {
+ // Verify no segments overlap
+ for (const Segment &sg1 : _file.segments) {
+ for (const Segment &sg2 : _file.segments) {
+ if (&sg1 == &sg2)
+ continue;
+ if (overlaps(sg1,sg2)) {
+ _ec = make_error_code(llvm::errc::executable_format_error);
+ return;
+ }
+ }
+ }
+
+ // Verify no sections overlap
+ for (const Section &s1 : _file.sections) {
+ for (const Section &s2 : _file.sections) {
+ if (&s1 == &s2)
+ continue;
+ if (overlaps(s1,s2)) {
+ _ec = make_error_code(llvm::errc::executable_format_error);
+ return;
+ }
+ }
+ }
+
+ // Build side table of extra info about segments and sections.
+ SegExtraInfo t;
+ t.fileOffset = 0;
+ for (const Segment &sg : _file.segments) {
+ _segInfo[&sg] = t;
+ }
+ SectionExtraInfo t2;
+ t2.fileOffset = 0;
+ // Assign sections to segments.
+ for (const Section &s : _file.sections) {
+ _sectInfo[&s] = t2;
+ bool foundSegment = false;
+ for (const Segment &sg : _file.segments) {
+ if (sg.name.equals(s.segmentName)) {
+ if ((s.address >= sg.address)
+ && (s.address+s.content.size() <= sg.address+sg.size)) {
+ _segInfo[&sg].sections.push_back(&s);
+ foundSegment = true;
+ break;
+ }
+ }
+ }
+ if (!foundSegment) {
+ _ec = make_error_code(llvm::errc::executable_format_error);
+ return;
+ }
+ }
+
+ // Assign file offsets.
+ uint32_t fileOffset = 0;
+ DEBUG_WITH_TYPE("MachOFileLayout",
+ llvm::dbgs() << "buildFileOffsets()\n");
+ for (const Segment &sg : _file.segments) {
+ _segInfo[&sg].fileOffset = fileOffset;
+ if ((_seg1addr == INT64_MAX) && sg.init_access)
+ _seg1addr = sg.address;
+ DEBUG_WITH_TYPE("MachOFileLayout",
+ llvm::dbgs() << " segment=" << sg.name
+ << ", fileOffset=" << _segInfo[&sg].fileOffset << "\n");
+
+ uint32_t segFileSize = 0;
+ // A segment that is not zero-fill must use a least one page of disk space.
+ if (sg.init_access)
+ segFileSize = _file.pageSize;
+ for (const Section *s : _segInfo[&sg].sections) {
+ uint32_t sectOffset = s->address - sg.address;
+ uint32_t sectFileSize =
+ isZeroFillSection(s->type) ? 0 : s->content.size();
+ segFileSize = std::max(segFileSize, sectOffset + sectFileSize);
+
+ _sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset;
+ DEBUG_WITH_TYPE("MachOFileLayout",
+ llvm::dbgs() << " section=" << s->sectionName
+ << ", fileOffset=" << fileOffset << "\n");
+ }
+
+ // round up all segments to page aligned, except __LINKEDIT
+ if (!sg.name.equals("__LINKEDIT")) {
+ _segInfo[&sg].fileSize = llvm::alignTo(segFileSize, _file.pageSize);
+ fileOffset = llvm::alignTo(fileOffset + segFileSize, _file.pageSize);
+ }
+ _addressOfLinkEdit = sg.address + sg.size;
+ }
+ _startOfLinkEdit = fileOffset;
+}
+
+size_t MachOFileLayout::size() const {
+ return _endOfSymbolStrings;
+}
+
+void MachOFileLayout::writeMachHeader() {
+ auto cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch);
+ // dynamic x86 executables on newer OS version should also set the
+ // CPU_SUBTYPE_LIB64 mask in the CPU subtype.
+ // FIXME: Check that this is a dynamic executable, not a static one.
+ if (_file.fileType == llvm::MachO::MH_EXECUTE &&
+ cpusubtype == CPU_SUBTYPE_X86_64_ALL &&
+ _file.os == MachOLinkingContext::OS::macOSX) {
+ uint32_t version;
+ bool failed = MachOLinkingContext::parsePackedVersion("10.5", version);
+ if (!failed && _file.minOSverson >= version)
+ cpusubtype |= CPU_SUBTYPE_LIB64;
+ }
+
+ mach_header *mh = reinterpret_cast<mach_header*>(_buffer);
+ mh->magic = _is64 ? llvm::MachO::MH_MAGIC_64 : llvm::MachO::MH_MAGIC;
+ mh->cputype = MachOLinkingContext::cpuTypeFromArch(_file.arch);
+ mh->cpusubtype = cpusubtype;
+ mh->filetype = _file.fileType;
+ mh->ncmds = _countOfLoadCommands;
+ mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands;
+ mh->flags = _file.flags;
+ if (_swap)
+ swapStruct(*mh);
+}
+
+uint32_t MachOFileLayout::indirectSymbolIndex(const Section §,
+ uint32_t &index) {
+ if (sect.indirectSymbols.empty())
+ return 0;
+ uint32_t result = index;
+ index += sect.indirectSymbols.size();
+ return result;
+}
+
+uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) {
+ if (sect.indirectSymbols.empty())
+ return 0;
+ if (sect.type != S_SYMBOL_STUBS)
+ return 0;
+ return sect.content.size() / sect.indirectSymbols.size();
+}
+
+template <typename T>
+llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
+ typename T::command* seg = reinterpret_cast<typename T::command*>(lc);
+ seg->cmd = T::LC;
+ seg->cmdsize = sizeof(typename T::command)
+ + _file.sections.size() * sizeof(typename T::section);
+ uint8_t *next = lc + seg->cmdsize;
+ memset(seg->segname, 0, 16);
+ seg->flags = 0;
+ seg->vmaddr = 0;
+ seg->fileoff = _endOfLoadCommands;
+ seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
+ seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
+ seg->nsects = _file.sections.size();
+ if (seg->nsects) {
+ seg->vmsize = _file.sections.back().address
+ + _file.sections.back().content.size();
+ seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
+ _file.sections.back().content.size() -
+ _sectInfo[&_file.sections.front()].fileOffset;
+ }
+ if (_swap)
+ swapStruct(*seg);
+ typename T::section *sout = reinterpret_cast<typename T::section*>
+ (lc+sizeof(typename T::command));
+ uint32_t relOffset = _startOfRelocations;
+ uint32_t indirectSymRunningIndex = 0;
+ for (const Section &sin : _file.sections) {
+ setString16(sin.sectionName, sout->sectname);
+ setString16(sin.segmentName, sout->segname);
+ sout->addr = sin.address;
+ sout->size = sin.content.size();
+ sout->offset = _sectInfo[&sin].fileOffset;
+ sout->align = llvm::Log2_32(sin.alignment);
+ sout->reloff = sin.relocations.empty() ? 0 : relOffset;
+ sout->nreloc = sin.relocations.size();
+ sout->flags = sin.type | sin.attributes;
+ sout->reserved1 = indirectSymbolIndex(sin, indirectSymRunningIndex);
+ sout->reserved2 = indirectSymbolElementSize(sin);
+ relOffset += sin.relocations.size() * sizeof(any_relocation_info);
+ if (_swap)
+ swapStruct(*sout);
+ ++sout;
+ }
+ lc = next;
+ return llvm::Error::success();
+}
+
+template <typename T>
+llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
+ uint32_t indirectSymRunningIndex = 0;
+ for (const Segment &seg : _file.segments) {
+ // Link edit has no sections and a custom range of address, so handle it
+ // specially.
+ SegExtraInfo &segInfo = _segInfo[&seg];
+ if (seg.name.equals("__LINKEDIT")) {
+ size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit;
+ typename T::command* cmd = reinterpret_cast<typename T::command*>(lc);
+ cmd->cmd = T::LC;
+ cmd->cmdsize = sizeof(typename T::command);
+ uint8_t *next = lc + cmd->cmdsize;
+ setString16("__LINKEDIT", cmd->segname);
+ cmd->vmaddr = _addressOfLinkEdit;
+ cmd->vmsize = llvm::alignTo(linkeditSize, _file.pageSize);
+ cmd->fileoff = _startOfLinkEdit;
+ cmd->filesize = linkeditSize;
+ cmd->initprot = seg.init_access;
+ cmd->maxprot = seg.max_access;
+ cmd->nsects = 0;
+ cmd->flags = 0;
+ if (_swap)
+ swapStruct(*cmd);
+ lc = next;
+ continue;
+ }
+ // Write segment command with trailing sections.
+ typename T::command* cmd = reinterpret_cast<typename T::command*>(lc);
+ cmd->cmd = T::LC;
+ cmd->cmdsize = sizeof(typename T::command)
+ + segInfo.sections.size() * sizeof(typename T::section);
+ uint8_t *next = lc + cmd->cmdsize;
+ setString16(seg.name, cmd->segname);
+ cmd->vmaddr = seg.address;
+ cmd->vmsize = seg.size;
+ cmd->fileoff = segInfo.fileOffset;
+ cmd->filesize = segInfo.fileSize;
+ cmd->initprot = seg.init_access;
+ cmd->maxprot = seg.max_access;
+ cmd->nsects = segInfo.sections.size();
+ cmd->flags = 0;
+ if (_swap)
+ swapStruct(*cmd);
+ typename T::section *sect = reinterpret_cast<typename T::section*>
+ (lc+sizeof(typename T::command));
+ for (const Section *section : segInfo.sections) {
+ setString16(section->sectionName, sect->sectname);
+ setString16(section->segmentName, sect->segname);
+ sect->addr = section->address;
+ sect->size = section->content.size();
+ if (isZeroFillSection(section->type))
+ sect->offset = 0;
+ else
+ sect->offset = section->address - seg.address + segInfo.fileOffset;
+ sect->align = llvm::Log2_32(section->alignment);
+ sect->reloff = 0;
+ sect->nreloc = 0;
+ sect->flags = section->type | section->attributes;
+ sect->reserved1 = indirectSymbolIndex(*section, indirectSymRunningIndex);
+ sect->reserved2 = indirectSymbolElementSize(*section);
+ if (_swap)
+ swapStruct(*sect);
+ ++sect;
+ }
+ lc = reinterpret_cast<uint8_t*>(next);
+ }
+ return llvm::Error::success();
+}
+
+static void writeVersionMinLoadCommand(const NormalizedFile &_file,
+ bool _swap,
+ uint8_t *&lc) {
+ if (!_file.hasMinVersionLoadCommand)
+ return;
+ version_min_command *vm = reinterpret_cast<version_min_command*>(lc);
+ switch (_file.os) {
+ case MachOLinkingContext::OS::unknown:
+ vm->cmd = _file.minOSVersionKind;
+ vm->cmdsize = sizeof(version_min_command);
+ vm->version = _file.minOSverson;
+ vm->sdk = 0;
+ break;
+ case MachOLinkingContext::OS::macOSX:
+ vm->cmd = LC_VERSION_MIN_MACOSX;
+ vm->cmdsize = sizeof(version_min_command);
+ vm->version = _file.minOSverson;
+ vm->sdk = _file.sdkVersion;
+ break;
+ case MachOLinkingContext::OS::iOS:
+ case MachOLinkingContext::OS::iOS_simulator:
+ vm->cmd = LC_VERSION_MIN_IPHONEOS;
+ vm->cmdsize = sizeof(version_min_command);
+ vm->version = _file.minOSverson;
+ vm->sdk = _file.sdkVersion;
+ break;
+ }
+ if (_swap)
+ swapStruct(*vm);
+ lc += sizeof(version_min_command);
+}
+
+llvm::Error MachOFileLayout::writeLoadCommands() {
+ uint8_t *lc = &_buffer[_startOfLoadCommands];
+ if (_file.fileType == llvm::MachO::MH_OBJECT) {
+ // Object files have one unnamed segment which holds all sections.
+ if (_is64) {
+ if (auto ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc))
+ return ec;
+ } else {
+ if (auto ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc))
+ return ec;
+ }
+ // Add LC_SYMTAB with symbol table info
+ symtab_command* st = reinterpret_cast<symtab_command*>(lc);
+ st->cmd = LC_SYMTAB;
+ st->cmdsize = sizeof(symtab_command);
+ st->symoff = _startOfSymbols;
+ st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
+ _file.globalSymbols.size() + _file.undefinedSymbols.size();
+ st->stroff = _startOfSymbolStrings;
+ st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
+ if (_swap)
+ swapStruct(*st);
+ lc += sizeof(symtab_command);
+
+ // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
+ // LC_VERSION_MIN_WATCHOS, LC_VERSION_MIN_TVOS
+ writeVersionMinLoadCommand(_file, _swap, lc);
+
+ // Add LC_FUNCTION_STARTS if needed.
+ if (_functionStartsSize != 0) {
+ linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
+ dl->cmd = LC_FUNCTION_STARTS;
+ dl->cmdsize = sizeof(linkedit_data_command);
+ dl->dataoff = _startOfFunctionStarts;
+ dl->datasize = _functionStartsSize;
+ if (_swap)
+ swapStruct(*dl);
+ lc += sizeof(linkedit_data_command);
+ }
+
+ // Add LC_DATA_IN_CODE if requested.
+ if (_file.generateDataInCodeLoadCommand) {
+ linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
+ dl->cmd = LC_DATA_IN_CODE;
+ dl->cmdsize = sizeof(linkedit_data_command);
+ dl->dataoff = _startOfDataInCode;
+ dl->datasize = _dataInCodeSize;
+ if (_swap)
+ swapStruct(*dl);
+ lc += sizeof(linkedit_data_command);
+ }
+ } else {
+ // Final linked images have sections under segments.
+ if (_is64) {
+ if (auto ec = writeSegmentLoadCommands<MachO64Trait>(lc))
+ return ec;
+ } else {
+ if (auto ec = writeSegmentLoadCommands<MachO32Trait>(lc))
+ return ec;
+ }
+
+ // Add LC_ID_DYLIB command for dynamic libraries.
+ if (_file.fileType == llvm::MachO::MH_DYLIB) {
+ dylib_command *dc = reinterpret_cast<dylib_command*>(lc);
+ StringRef path = _file.installName;
+ uint32_t size = sizeof(dylib_command) + pointerAlign(path.size() + 1);
+ dc->cmd = LC_ID_DYLIB;
+ dc->cmdsize = size;
+ dc->dylib.name = sizeof(dylib_command); // offset
+ // needs to be some constant value different than the one in LC_LOAD_DYLIB
+ dc->dylib.timestamp = 1;
+ dc->dylib.current_version = _file.currentVersion;
+ dc->dylib.compatibility_version = _file.compatVersion;
+ if (_swap)
+ swapStruct(*dc);
+ memcpy(lc + sizeof(dylib_command), path.begin(), path.size());
+ lc[sizeof(dylib_command) + path.size()] = '\0';
+ lc += size;
+ }
+
+ // Add LC_DYLD_INFO_ONLY.
+ dyld_info_command* di = reinterpret_cast<dyld_info_command*>(lc);
+ di->cmd = LC_DYLD_INFO_ONLY;
+ di->cmdsize = sizeof(dyld_info_command);
+ di->rebase_off = _rebaseInfo.size() ? _startOfRebaseInfo : 0;
+ di->rebase_size = _rebaseInfo.size();
+ di->bind_off = _bindingInfo.size() ? _startOfBindingInfo : 0;
+ di->bind_size = _bindingInfo.size();
+ di->weak_bind_off = 0;
+ di->weak_bind_size = 0;
+ di->lazy_bind_off = _lazyBindingInfo.size() ? _startOfLazyBindingInfo : 0;
+ di->lazy_bind_size = _lazyBindingInfo.size();
+ di->export_off = _exportTrie.size() ? _startOfExportTrie : 0;
+ di->export_size = _exportTrie.size();
+ if (_swap)
+ swapStruct(*di);
+ lc += sizeof(dyld_info_command);
+
+ // Add LC_SYMTAB with symbol table info.
+ symtab_command* st = reinterpret_cast<symtab_command*>(lc);
+ st->cmd = LC_SYMTAB;
+ st->cmdsize = sizeof(symtab_command);
+ st->symoff = _startOfSymbols;
+ st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
+ _file.globalSymbols.size() + _file.undefinedSymbols.size();
+ st->stroff = _startOfSymbolStrings;
+ st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
+ if (_swap)
+ swapStruct(*st);
+ lc += sizeof(symtab_command);
+
+ // Add LC_DYSYMTAB
+ if (_file.fileType != llvm::MachO::MH_PRELOAD) {
+ dysymtab_command* dst = reinterpret_cast<dysymtab_command*>(lc);
+ dst->cmd = LC_DYSYMTAB;
+ dst->cmdsize = sizeof(dysymtab_command);
+ dst->ilocalsym = _symbolTableLocalsStartIndex;
+ dst->nlocalsym = _file.stabsSymbols.size() +
+ _file.localSymbols.size();
+ dst->iextdefsym = _symbolTableGlobalsStartIndex;
+ dst->nextdefsym = _file.globalSymbols.size();
+ dst->iundefsym = _symbolTableUndefinesStartIndex;
+ dst->nundefsym = _file.undefinedSymbols.size();
+ dst->tocoff = 0;
+ dst->ntoc = 0;
+ dst->modtaboff = 0;
+ dst->nmodtab = 0;
+ dst->extrefsymoff = 0;
+ dst->nextrefsyms = 0;
+ dst->indirectsymoff = _startOfIndirectSymbols;
+ dst->nindirectsyms = _indirectSymbolTableCount;
+ dst->extreloff = 0;
+ dst->nextrel = 0;
+ dst->locreloff = 0;
+ dst->nlocrel = 0;
+ if (_swap)
+ swapStruct(*dst);
+ lc += sizeof(dysymtab_command);
+ }
+
+ // If main executable, add LC_LOAD_DYLINKER
+ if (_file.fileType == llvm::MachO::MH_EXECUTE) {
+ // Build LC_LOAD_DYLINKER load command.
+ uint32_t size=pointerAlign(sizeof(dylinker_command)+dyldPath().size()+1);
+ dylinker_command* dl = reinterpret_cast<dylinker_command*>(lc);
+ dl->cmd = LC_LOAD_DYLINKER;
+ dl->cmdsize = size;
+ dl->name = sizeof(dylinker_command); // offset
+ if (_swap)
+ swapStruct(*dl);
+ memcpy(lc+sizeof(dylinker_command), dyldPath().data(), dyldPath().size());
+ lc[sizeof(dylinker_command)+dyldPath().size()] = '\0';
+ lc += size;
+ }
+
+ // Add LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_VERSION_MIN_WATCHOS,
+ // LC_VERSION_MIN_TVOS
+ writeVersionMinLoadCommand(_file, _swap, lc);
+
+ // Add LC_SOURCE_VERSION
+ {
+ // Note, using a temporary here to appease UB as we may not be aligned
+ // enough for a struct containing a uint64_t when emitting a 32-bit binary
+ source_version_command sv;
+ sv.cmd = LC_SOURCE_VERSION;
+ sv.cmdsize = sizeof(source_version_command);
+ sv.version = _file.sourceVersion;
+ if (_swap)
+ swapStruct(sv);
+ memcpy(lc, &sv, sizeof(source_version_command));
+ lc += sizeof(source_version_command);
+ }
+
+ // If main executable, add LC_MAIN.
+ if (_file.fileType == llvm::MachO::MH_EXECUTE) {
+ // Build LC_MAIN load command.
+ // Note, using a temporary here to appease UB as we may not be aligned
+ // enough for a struct containing a uint64_t when emitting a 32-bit binary
+ entry_point_command ep;
+ ep.cmd = LC_MAIN;
+ ep.cmdsize = sizeof(entry_point_command);
+ ep.entryoff = _file.entryAddress - _seg1addr;
+ ep.stacksize = _file.stackSize;
+ if (_swap)
+ swapStruct(ep);
+ memcpy(lc, &ep, sizeof(entry_point_command));
+ lc += sizeof(entry_point_command);
+ }
+
+ // Add LC_LOAD_DYLIB commands
+ for (const DependentDylib &dep : _file.dependentDylibs) {
+ dylib_command* dc = reinterpret_cast<dylib_command*>(lc);
+ uint32_t size = sizeof(dylib_command) + pointerAlign(dep.path.size()+1);
+ dc->cmd = dep.kind;
+ dc->cmdsize = size;
+ dc->dylib.name = sizeof(dylib_command); // offset
+ // needs to be some constant value different than the one in LC_ID_DYLIB
+ dc->dylib.timestamp = 2;
+ dc->dylib.current_version = dep.currentVersion;
+ dc->dylib.compatibility_version = dep.compatVersion;
+ if (_swap)
+ swapStruct(*dc);
+ memcpy(lc+sizeof(dylib_command), dep.path.begin(), dep.path.size());
+ lc[sizeof(dylib_command)+dep.path.size()] = '\0';
+ lc += size;
+ }
+
+ // Add LC_RPATH
+ for (const StringRef &path : _file.rpaths) {
+ rpath_command *rpc = reinterpret_cast<rpath_command *>(lc);
+ uint32_t size = pointerAlign(sizeof(rpath_command) + path.size() + 1);
+ rpc->cmd = LC_RPATH;
+ rpc->cmdsize = size;
+ rpc->path = sizeof(rpath_command); // offset
+ if (_swap)
+ swapStruct(*rpc);
+ memcpy(lc+sizeof(rpath_command), path.begin(), path.size());
+ lc[sizeof(rpath_command)+path.size()] = '\0';
+ lc += size;
+ }
+
+ // Add LC_FUNCTION_STARTS if needed.
+ if (_functionStartsSize != 0) {
+ linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
+ dl->cmd = LC_FUNCTION_STARTS;
+ dl->cmdsize = sizeof(linkedit_data_command);
+ dl->dataoff = _startOfFunctionStarts;
+ dl->datasize = _functionStartsSize;
+ if (_swap)
+ swapStruct(*dl);
+ lc += sizeof(linkedit_data_command);
+ }
+
+ // Add LC_DATA_IN_CODE if requested.
+ if (_file.generateDataInCodeLoadCommand) {
+ linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc);
+ dl->cmd = LC_DATA_IN_CODE;
+ dl->cmdsize = sizeof(linkedit_data_command);
+ dl->dataoff = _startOfDataInCode;
+ dl->datasize = _dataInCodeSize;
+ if (_swap)
+ swapStruct(*dl);
+ lc += sizeof(linkedit_data_command);
+ }
+ }
+ assert(lc == &_buffer[_endOfLoadCommands]);
+ return llvm::Error::success();
+}
+
+void MachOFileLayout::writeSectionContent() {
+ for (const Section &s : _file.sections) {
+ // Copy all section content to output buffer.
+ if (isZeroFillSection(s.type))
+ continue;
+ if (s.content.empty())
+ continue;
+ uint32_t offset = _sectInfo[&s].fileOffset;
+ assert(offset >= _endOfLoadCommands);
+ uint8_t *p = &_buffer[offset];
+ memcpy(p, &s.content[0], s.content.size());
+ p += s.content.size();
+ }
+}
+
+void MachOFileLayout::writeRelocations() {
+ uint32_t relOffset = _startOfRelocations;
+ for (Section sect : _file.sections) {
+ for (Relocation r : sect.relocations) {
+ any_relocation_info* rb = reinterpret_cast<any_relocation_info*>(
+ &_buffer[relOffset]);
+ *rb = packRelocation(r, _swap, _bigEndianArch);
+ relOffset += sizeof(any_relocation_info);
+ }
+ }
+}
+
+void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols,
+ uint32_t &symOffset, uint32_t &strOffset) {
+ for (const Symbol &sym : symbols) {
+ if (_is64) {
+ nlist_64* nb = reinterpret_cast<nlist_64*>(&_buffer[symOffset]);
+ nb->n_strx = strOffset - _startOfSymbolStrings;
+ nb->n_type = sym.type | sym.scope;
+ nb->n_sect = sym.sect;
+ nb->n_desc = sym.desc;
+ nb->n_value = sym.value;
+ if (_swap)
+ swapStruct(*nb);
+ symOffset += sizeof(nlist_64);
+ } else {
+ nlist* nb = reinterpret_cast<nlist*>(&_buffer[symOffset]);
+ nb->n_strx = strOffset - _startOfSymbolStrings;
+ nb->n_type = sym.type | sym.scope;
+ nb->n_sect = sym.sect;
+ nb->n_desc = sym.desc;
+ nb->n_value = sym.value;
+ if (_swap)
+ swapStruct(*nb);
+ symOffset += sizeof(nlist);
+ }
+ memcpy(&_buffer[strOffset], sym.name.begin(), sym.name.size());
+ strOffset += sym.name.size();
+ _buffer[strOffset++] ='\0'; // Strings in table have nul terminator.
+ }
+}
+
+void MachOFileLayout::writeFunctionStartsInfo() {
+ if (!_functionStartsSize)
+ return;
+ memcpy(&_buffer[_startOfFunctionStarts], _file.functionStarts.data(),
+ _functionStartsSize);
+}
+
+void MachOFileLayout::writeDataInCodeInfo() {
+ uint32_t offset = _startOfDataInCode;
+ for (const DataInCode &entry : _file.dataInCode) {
+ data_in_code_entry *dst = reinterpret_cast<data_in_code_entry*>(
+ &_buffer[offset]);
+ dst->offset = entry.offset;
+ dst->length = entry.length;
+ dst->kind = entry.kind;
+ if (_swap)
+ swapStruct(*dst);
+ offset += sizeof(data_in_code_entry);
+ }
+}
+
+void MachOFileLayout::writeSymbolTable() {
+ // Write symbol table and symbol strings in parallel.
+ uint32_t symOffset = _startOfSymbols;
+ uint32_t strOffset = _startOfSymbolStrings;
+ // Reserve n_strx offset of zero to mean no name.
+ _buffer[strOffset++] = ' ';
+ _buffer[strOffset++] = '\0';
+ appendSymbols(_file.stabsSymbols, symOffset, strOffset);
+ appendSymbols(_file.localSymbols, symOffset, strOffset);
+ appendSymbols(_file.globalSymbols, symOffset, strOffset);
+ appendSymbols(_file.undefinedSymbols, symOffset, strOffset);
+ // Write indirect symbol table array.
+ uint32_t *indirects = reinterpret_cast<uint32_t*>
+ (&_buffer[_startOfIndirectSymbols]);
+ if (_file.fileType == llvm::MachO::MH_OBJECT) {
+ // Object files have sections in same order as input normalized file.
+ for (const Section §ion : _file.sections) {
+ for (uint32_t index : section.indirectSymbols) {
+ if (_swap)
+ *indirects++ = llvm::sys::getSwappedBytes(index);
+ else
+ *indirects++ = index;
+ }
+ }
+ } else {
+ // Final linked images must sort sections from normalized file.
+ for (const Segment &seg : _file.segments) {
+ SegExtraInfo &segInfo = _segInfo[&seg];
+ for (const Section *section : segInfo.sections) {
+ for (uint32_t index : section->indirectSymbols) {
+ if (_swap)
+ *indirects++ = llvm::sys::getSwappedBytes(index);
+ else
+ *indirects++ = index;
+ }
+ }
+ }
+ }
+}
+
+void MachOFileLayout::writeRebaseInfo() {
+ memcpy(&_buffer[_startOfRebaseInfo], _rebaseInfo.bytes(), _rebaseInfo.size());
+}
+
+void MachOFileLayout::writeBindingInfo() {
+ memcpy(&_buffer[_startOfBindingInfo],
+ _bindingInfo.bytes(), _bindingInfo.size());
+}
+
+void MachOFileLayout::writeLazyBindingInfo() {
+ memcpy(&_buffer[_startOfLazyBindingInfo],
+ _lazyBindingInfo.bytes(), _lazyBindingInfo.size());
+}
+
+void MachOFileLayout::writeExportInfo() {
+ memcpy(&_buffer[_startOfExportTrie], _exportTrie.bytes(), _exportTrie.size());
+}
+
+void MachOFileLayout::buildLinkEditInfo() {
+ buildRebaseInfo();
+ buildBindInfo();
+ buildLazyBindInfo();
+ buildExportTrie();
+ computeSymbolTableSizes();
+ computeFunctionStartsSize();
+ computeDataInCodeSize();
+}
+
+void MachOFileLayout::buildSectionRelocations() {
+
+}
+
+void MachOFileLayout::buildRebaseInfo() {
+ // TODO: compress rebasing info.
+ for (const RebaseLocation& entry : _file.rebasingInfo) {
+ _rebaseInfo.append_byte(REBASE_OPCODE_SET_TYPE_IMM | entry.kind);
+ _rebaseInfo.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+ | entry.segIndex);
+ _rebaseInfo.append_uleb128(entry.segOffset);
+ _rebaseInfo.append_uleb128(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1);
+ }
+ _rebaseInfo.append_byte(REBASE_OPCODE_DONE);
+ _rebaseInfo.align(_is64 ? 8 : 4);
+}
+
+void MachOFileLayout::buildBindInfo() {
+ // TODO: compress bind info.
+ uint64_t lastAddend = 0;
+ int lastOrdinal = 0x80000000;
+ StringRef lastSymbolName;
+ BindType lastType = (BindType)0;
+ Hex32 lastSegOffset = ~0U;
+ uint8_t lastSegIndex = (uint8_t)~0U;
+ for (const BindLocation& entry : _file.bindingInfo) {
+ if (entry.ordinal != lastOrdinal) {
+ if (entry.ordinal <= 0)
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
+ (entry.ordinal & BIND_IMMEDIATE_MASK));
+ else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ entry.ordinal);
+ else {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ _bindingInfo.append_uleb128(entry.ordinal);
+ }
+ lastOrdinal = entry.ordinal;
+ }
+
+ if (lastSymbolName != entry.symbolName) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
+ _bindingInfo.append_string(entry.symbolName);
+ lastSymbolName = entry.symbolName;
+ }
+
+ if (lastType != entry.kind) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
+ lastType = entry.kind;
+ }
+
+ if (lastSegIndex != entry.segIndex || lastSegOffset != entry.segOffset) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+ | entry.segIndex);
+ _bindingInfo.append_uleb128(entry.segOffset);
+ lastSegIndex = entry.segIndex;
+ lastSegOffset = entry.segOffset;
+ }
+ if (entry.addend != lastAddend) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
+ _bindingInfo.append_sleb128(entry.addend);
+ lastAddend = entry.addend;
+ }
+ _bindingInfo.append_byte(BIND_OPCODE_DO_BIND);
+ }
+ _bindingInfo.append_byte(BIND_OPCODE_DONE);
+ _bindingInfo.align(_is64 ? 8 : 4);
+}
+
+void MachOFileLayout::buildLazyBindInfo() {
+ for (const BindLocation& entry : _file.lazyBindingInfo) {
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+ | entry.segIndex);
+ _lazyBindingInfo.append_uleb128(entry.segOffset);
+ if (entry.ordinal <= 0)
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
+ (entry.ordinal & BIND_IMMEDIATE_MASK));
+ else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ entry.ordinal);
+ else {
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ _lazyBindingInfo.append_uleb128(entry.ordinal);
+ }
+ // FIXME: We need to | the opcode here with flags.
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
+ _lazyBindingInfo.append_string(entry.symbolName);
+ _lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND);
+ _lazyBindingInfo.append_byte(BIND_OPCODE_DONE);
+ }
+ _lazyBindingInfo.align(_is64 ? 8 : 4);
+}
+
+void TrieNode::addSymbol(const Export& entry,
+ BumpPtrAllocator &allocator,
+ std::vector<TrieNode*> &allNodes) {
+ StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
+ for (TrieEdge &edge : _children) {
+ StringRef edgeStr = edge._subString;
+ if (partialStr.startswith(edgeStr)) {
+ // Already have matching edge, go down that path.
+ edge._child->addSymbol(entry, allocator, allNodes);
+ return;
+ }
+ // See if string has common prefix with existing edge.
+ for (int n=edgeStr.size()-1; n > 0; --n) {
+ if (partialStr.substr(0, n).equals(edgeStr.substr(0, n))) {
+ // Splice in new node: was A -> C, now A -> B -> C
+ StringRef bNodeStr = edge._child->_cummulativeString;
+ bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator);
+ auto *bNode = new (allocator) TrieNode(bNodeStr);
+ allNodes.push_back(bNode);
+ TrieNode* cNode = edge._child;
+ StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator);
+ StringRef bcEdgeStr = edgeStr.substr(n).copy(allocator);
+ DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
+ << "splice in TrieNode('" << bNodeStr
+ << "') between edge '"
+ << abEdgeStr << "' and edge='"
+ << bcEdgeStr<< "'\n");
+ TrieEdge& abEdge = edge;
+ abEdge._subString = abEdgeStr;
+ abEdge._child = bNode;
+ auto *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode);
+ bNode->_children.insert(bNode->_children.end(), bcEdge);
+ bNode->addSymbol(entry, allocator, allNodes);
+ return;
+ }
+ }
+ }
+ if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ assert(entry.otherOffset != 0);
+ }
+ if (entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
+ assert(entry.otherOffset != 0);
+ }
+ // No commonality with any existing child, make a new edge.
+ auto *newNode = new (allocator) TrieNode(entry.name.copy(allocator));
+ auto *newEdge = new (allocator) TrieEdge(partialStr, newNode);
+ _children.insert(_children.end(), newEdge);
+ DEBUG_WITH_TYPE("trie-builder", llvm::dbgs()
+ << "new TrieNode('" << entry.name << "') with edge '"
+ << partialStr << "' from node='"
+ << _cummulativeString << "'\n");
+ newNode->_address = entry.offset;
+ newNode->_flags = entry.flags | entry.kind;
+ newNode->_other = entry.otherOffset;
+ if ((entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && !entry.otherName.empty())
+ newNode->_importedName = entry.otherName.copy(allocator);
+ newNode->_hasExportInfo = true;
+ allNodes.push_back(newNode);
+}
+
+void TrieNode::addOrderedNodes(const Export& entry,
+ std::vector<TrieNode*> &orderedNodes) {
+ if (!_ordered) {
+ orderedNodes.push_back(this);
+ _ordered = true;
+ }
+
+ StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
+ for (TrieEdge &edge : _children) {
+ StringRef edgeStr = edge._subString;
+ if (partialStr.startswith(edgeStr)) {
+ // Already have matching edge, go down that path.
+ edge._child->addOrderedNodes(entry, orderedNodes);
+ return;
+ }
+ }
+}
+
+bool TrieNode::updateOffset(uint32_t& offset) {
+ uint32_t nodeSize = 1; // Length when no export info
+ if (_hasExportInfo) {
+ if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ nodeSize = llvm::getULEB128Size(_flags);
+ nodeSize += llvm::getULEB128Size(_other); // Other contains ordinal.
+ nodeSize += _importedName.size();
+ ++nodeSize; // Trailing zero in imported name.
+ } else {
+ nodeSize = llvm::getULEB128Size(_flags) + llvm::getULEB128Size(_address);
+ if (_flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER)
+ nodeSize += llvm::getULEB128Size(_other);
+ }
+ // Overall node size so far is uleb128 of export info + actual export info.
+ nodeSize += llvm::getULEB128Size(nodeSize);
+ }
+ // Compute size of all child edges.
+ ++nodeSize; // Byte for number of children.
+ for (TrieEdge &edge : _children) {
+ nodeSize += edge._subString.size() + 1 // String length.
+ + llvm::getULEB128Size(edge._child->_trieOffset); // Offset len.
+ }
+ // On input, 'offset' is new prefered location for this node.
+ bool result = (_trieOffset != offset);
+ // Store new location in node object for use by parents.
+ _trieOffset = offset;
+ // Update offset for next iteration.
+ offset += nodeSize;
+ // Return true if _trieOffset was changed.
+ return result;
+}
+
+void TrieNode::appendToByteBuffer(ByteBuffer &out) {
+ if (_hasExportInfo) {
+ if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ if (!_importedName.empty()) {
+ // nodes with re-export info: size, flags, ordinal, import-name
+ uint32_t nodeSize = llvm::getULEB128Size(_flags)
+ + llvm::getULEB128Size(_other)
+ + _importedName.size() + 1;
+ assert(nodeSize < 256);
+ out.append_byte(nodeSize);
+ out.append_uleb128(_flags);
+ out.append_uleb128(_other);
+ out.append_string(_importedName);
+ } else {
+ // nodes without re-export info: size, flags, ordinal, empty-string
+ uint32_t nodeSize = llvm::getULEB128Size(_flags)
+ + llvm::getULEB128Size(_other) + 1;
+ assert(nodeSize < 256);
+ out.append_byte(nodeSize);
+ out.append_uleb128(_flags);
+ out.append_uleb128(_other);
+ out.append_byte(0);
+ }
+ } else if ( _flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+ // Nodes with export info: size, flags, address, other
+ uint32_t nodeSize = llvm::getULEB128Size(_flags)
+ + llvm::getULEB128Size(_address)
+ + llvm::getULEB128Size(_other);
+ assert(nodeSize < 256);
+ out.append_byte(nodeSize);
+ out.append_uleb128(_flags);
+ out.append_uleb128(_address);
+ out.append_uleb128(_other);
+ } else {
+ // Nodes with export info: size, flags, address
+ uint32_t nodeSize = llvm::getULEB128Size(_flags)
+ + llvm::getULEB128Size(_address);
+ assert(nodeSize < 256);
+ out.append_byte(nodeSize);
+ out.append_uleb128(_flags);
+ out.append_uleb128(_address);
+ }
+ } else {
+ // Node with no export info.
+ uint32_t nodeSize = 0;
+ out.append_byte(nodeSize);
+ }
+ // Add number of children.
+ assert(_children.size() < 256);
+ out.append_byte(_children.size());
+ // Append each child edge substring and node offset.
+ for (TrieEdge &edge : _children) {
+ out.append_string(edge._subString);
+ out.append_uleb128(edge._child->_trieOffset);
+ }
+}
+
+void MachOFileLayout::buildExportTrie() {
+ if (_file.exportInfo.empty())
+ return;
+
+ // For all temporary strings and objects used building trie.
+ BumpPtrAllocator allocator;
+
+ // Build trie of all exported symbols.
+ auto *rootNode = new (allocator) TrieNode(StringRef());
+ std::vector<TrieNode*> allNodes;
+ allNodes.reserve(_file.exportInfo.size()*2);
+ allNodes.push_back(rootNode);
+ for (const Export& entry : _file.exportInfo) {
+ rootNode->addSymbol(entry, allocator, allNodes);
+ }
+
+ std::vector<TrieNode*> orderedNodes;
+ orderedNodes.reserve(allNodes.size());
+
+ for (const Export& entry : _file.exportInfo)
+ rootNode->addOrderedNodes(entry, orderedNodes);
+
+ // Assign each node in the vector an offset in the trie stream, iterating
+ // until all uleb128 sizes have stabilized.
+ bool more;
+ do {
+ uint32_t offset = 0;
+ more = false;
+ for (TrieNode* node : orderedNodes) {
+ if (node->updateOffset(offset))
+ more = true;
+ }
+ } while (more);
+
+ // Serialize trie to ByteBuffer.
+ for (TrieNode* node : orderedNodes) {
+ node->appendToByteBuffer(_exportTrie);
+ }
+ _exportTrie.align(_is64 ? 8 : 4);
+}
+
+void MachOFileLayout::computeSymbolTableSizes() {
+ // MachO symbol tables have three ranges: locals, globals, and undefines
+ const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist));
+ _symbolTableSize = nlistSize * (_file.stabsSymbols.size()
+ + _file.localSymbols.size()
+ + _file.globalSymbols.size()
+ + _file.undefinedSymbols.size());
+ // Always reserve 1-byte for the empty string and 1-byte for its terminator.
+ _symbolStringPoolSize = 2;
+ for (const Symbol &sym : _file.stabsSymbols) {
+ _symbolStringPoolSize += (sym.name.size()+1);
+ }
+ for (const Symbol &sym : _file.localSymbols) {
+ _symbolStringPoolSize += (sym.name.size()+1);
+ }
+ for (const Symbol &sym : _file.globalSymbols) {
+ _symbolStringPoolSize += (sym.name.size()+1);
+ }
+ for (const Symbol &sym : _file.undefinedSymbols) {
+ _symbolStringPoolSize += (sym.name.size()+1);
+ }
+ _symbolTableLocalsStartIndex = 0;
+ _symbolTableGlobalsStartIndex = _file.stabsSymbols.size() +
+ _file.localSymbols.size();
+ _symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex
+ + _file.globalSymbols.size();
+
+ _indirectSymbolTableCount = 0;
+ for (const Section § : _file.sections) {
+ _indirectSymbolTableCount += sect.indirectSymbols.size();
+ }
+}
+
+void MachOFileLayout::computeFunctionStartsSize() {
+ _functionStartsSize = _file.functionStarts.size();
+}
+
+void MachOFileLayout::computeDataInCodeSize() {
+ _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry);
+}
+
+void MachOFileLayout::writeLinkEditContent() {
+ if (_file.fileType == llvm::MachO::MH_OBJECT) {
+ writeRelocations();
+ writeFunctionStartsInfo();
+ writeDataInCodeInfo();
+ writeSymbolTable();
+ } else {
+ writeRebaseInfo();
+ writeBindingInfo();
+ writeLazyBindingInfo();
+ // TODO: add weak binding info
+ writeExportInfo();
+ writeFunctionStartsInfo();
+ writeDataInCodeInfo();
+ writeSymbolTable();
+ }
+}
+
+llvm::Error MachOFileLayout::writeBinary(StringRef path) {
+ // Check for pending error from constructor.
+ if (_ec)
+ return llvm::errorCodeToError(_ec);
+ // Create FileOutputBuffer with calculated size.
+ unsigned flags = 0;
+ if (_file.fileType != llvm::MachO::MH_OBJECT)
+ flags = llvm::FileOutputBuffer::F_executable;
+ Expected<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr =
+ llvm::FileOutputBuffer::create(path, size(), flags);
+ if (Error E = fobOrErr.takeError())
+ return E;
+ std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr;
+ // Write content.
+ _buffer = fob->getBufferStart();
+ writeMachHeader();
+ if (auto ec = writeLoadCommands())
+ return ec;
+ writeSectionContent();
+ writeLinkEditContent();
+ if (Error E = fob->commit())
+ return E;
+
+ return llvm::Error::success();
+}
+
+/// Takes in-memory normalized view and writes a mach-o object file.
+llvm::Error writeBinary(const NormalizedFile &file, StringRef path) {
+ MachOFileLayout layout(file, false);
+ return layout.writeBinary(path);
+}
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file Converts from in-memory Atoms to in-memory normalized mach-o.
+///
+/// +------------+
+/// | normalized |
+/// +------------+
+/// ^
+/// |
+/// |
+/// +-------+
+/// | Atoms |
+/// +-------+
+
+#include "ArchHandler.h"
+#include "DebugInfo.h"
+#include "MachONormalizedFile.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include <map>
+#include <system_error>
+#include <unordered_set>
+
+using llvm::StringRef;
+using llvm::isa;
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+using namespace lld;
+
+namespace {
+
+struct AtomInfo {
+ const DefinedAtom *atom;
+ uint64_t offsetInSection;
+};
+
+struct SectionInfo {
+ SectionInfo(StringRef seg, StringRef sect, SectionType type,
+ const MachOLinkingContext &ctxt, uint32_t attr,
+ bool relocsToDefinedCanBeImplicit);
+
+ StringRef segmentName;
+ StringRef sectionName;
+ SectionType type;
+ uint32_t attributes;
+ uint64_t address;
+ uint64_t size;
+ uint16_t alignment;
+
+ /// If this is set, the any relocs in this section which point to defined
+ /// addresses can be implicitly generated. This is the case for the
+ /// __eh_frame section where references to the function can be implicit if the
+ /// function is defined.
+ bool relocsToDefinedCanBeImplicit;
+
+
+ std::vector<AtomInfo> atomsAndOffsets;
+ uint32_t normalizedSectionIndex;
+ uint32_t finalSectionIndex;
+};
+
+SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t,
+ const MachOLinkingContext &ctxt, uint32_t attrs,
+ bool relocsToDefinedCanBeImplicit)
+ : segmentName(sg), sectionName(sct), type(t), attributes(attrs),
+ address(0), size(0), alignment(1),
+ relocsToDefinedCanBeImplicit(relocsToDefinedCanBeImplicit),
+ normalizedSectionIndex(0), finalSectionIndex(0) {
+ uint16_t align = 1;
+ if (ctxt.sectionAligned(segmentName, sectionName, align)) {
+ alignment = align;
+ }
+}
+
+struct SegmentInfo {
+ SegmentInfo(StringRef name);
+
+ StringRef name;
+ uint64_t address;
+ uint64_t size;
+ uint32_t init_access;
+ uint32_t max_access;
+ std::vector<SectionInfo*> sections;
+ uint32_t normalizedSegmentIndex;
+};
+
+SegmentInfo::SegmentInfo(StringRef n)
+ : name(n), address(0), size(0), init_access(0), max_access(0),
+ normalizedSegmentIndex(0) {
+}
+
+class Util {
+public:
+ Util(const MachOLinkingContext &ctxt)
+ : _ctx(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr),
+ _hasTLVDescriptors(false), _subsectionsViaSymbols(true) {}
+ ~Util();
+
+ void processDefinedAtoms(const lld::File &atomFile);
+ void processAtomAttributes(const DefinedAtom *atom);
+ void assignAtomToSection(const DefinedAtom *atom);
+ void organizeSections();
+ void assignAddressesToSections(const NormalizedFile &file);
+ uint32_t fileFlags();
+ void copySegmentInfo(NormalizedFile &file);
+ void copySectionInfo(NormalizedFile &file);
+ void updateSectionInfo(NormalizedFile &file);
+ void buildAtomToAddressMap();
+ llvm::Error synthesizeDebugNotes(NormalizedFile &file);
+ llvm::Error addSymbols(const lld::File &atomFile, NormalizedFile &file);
+ void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file);
+ void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file);
+ void addExportInfo(const lld::File &, NormalizedFile &file);
+ void addSectionRelocs(const lld::File &, NormalizedFile &file);
+ void addFunctionStarts(const lld::File &, NormalizedFile &file);
+ void buildDataInCodeArray(const lld::File &, NormalizedFile &file);
+ void addDependentDylibs(const lld::File &, NormalizedFile &file);
+ void copyEntryPointAddress(NormalizedFile &file);
+ void copySectionContent(NormalizedFile &file);
+
+ bool allSourceFilesHaveMinVersions() const {
+ return _allSourceFilesHaveMinVersions;
+ }
+
+ uint32_t minVersion() const {
+ return _minVersion;
+ }
+
+ LoadCommandType minVersionCommandType() const {
+ return _minVersionCommandType;
+ }
+
+private:
+ typedef std::map<DefinedAtom::ContentType, SectionInfo*> TypeToSection;
+ typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress;
+
+ struct DylibInfo { int ordinal; bool hasWeak; bool hasNonWeak; };
+ typedef llvm::StringMap<DylibInfo> DylibPathToInfo;
+
+ SectionInfo *sectionForAtom(const DefinedAtom*);
+ SectionInfo *getRelocatableSection(DefinedAtom::ContentType type);
+ SectionInfo *getFinalSection(DefinedAtom::ContentType type);
+ void appendAtom(SectionInfo *sect, const DefinedAtom *atom);
+ SegmentInfo *segmentForName(StringRef segName);
+ void layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr);
+ void layoutSectionsInTextSegment(size_t, SegmentInfo *, uint64_t &);
+ void copySectionContent(SectionInfo *si, ContentBytes &content);
+ uint16_t descBits(const DefinedAtom* atom);
+ int dylibOrdinal(const SharedLibraryAtom *sa);
+ void segIndexForSection(const SectionInfo *sect,
+ uint8_t &segmentIndex, uint64_t &segmentStartAddr);
+ const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom);
+ const Atom *targetOfStub(const DefinedAtom *stubAtom);
+ llvm::Error getSymbolTableRegion(const DefinedAtom* atom,
+ bool &inGlobalsRegion,
+ SymbolScope &symbolScope);
+ void appendSection(SectionInfo *si, NormalizedFile &file);
+ uint32_t sectionIndexForAtom(const Atom *atom);
+ void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
+ NormalizedFile &file);
+
+ typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex;
+ struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; };
+ struct AtomSorter {
+ bool operator()(const AtomAndIndex &left, const AtomAndIndex &right);
+ };
+ struct SegmentSorter {
+ bool operator()(const SegmentInfo *left, const SegmentInfo *right);
+ static unsigned weight(const SegmentInfo *);
+ };
+ struct TextSectionSorter {
+ bool operator()(const SectionInfo *left, const SectionInfo *right);
+ static unsigned weight(const SectionInfo *);
+ };
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ llvm::BumpPtrAllocator _allocator;
+ std::vector<SectionInfo*> _sectionInfos;
+ std::vector<SegmentInfo*> _segmentInfos;
+ TypeToSection _sectionMap;
+ std::vector<SectionInfo*> _customSections;
+ AtomToAddress _atomToAddress;
+ DylibPathToInfo _dylibInfo;
+ const DefinedAtom *_entryAtom;
+ AtomToIndex _atomToSymbolIndex;
+ std::vector<const Atom *> _machHeaderAliasAtoms;
+ bool _hasTLVDescriptors;
+ bool _subsectionsViaSymbols;
+ bool _allSourceFilesHaveMinVersions = true;
+ LoadCommandType _minVersionCommandType = (LoadCommandType)0;
+ uint32_t _minVersion = 0;
+ std::vector<lld::mach_o::Stab> _stabs;
+};
+
+Util::~Util() {
+ // The SectionInfo structs are BumpPtr allocated, but atomsAndOffsets needs
+ // to be deleted.
+ for (SectionInfo *si : _sectionInfos) {
+ // clear() destroys vector elements, but does not deallocate.
+ // Instead use swap() to deallocate vector buffer.
+ std::vector<AtomInfo> empty;
+ si->atomsAndOffsets.swap(empty);
+ }
+ // The SegmentInfo structs are BumpPtr allocated, but sections needs
+ // to be deleted.
+ for (SegmentInfo *sgi : _segmentInfos) {
+ std::vector<SectionInfo*> empty2;
+ sgi->sections.swap(empty2);
+ }
+}
+
+SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) {
+ StringRef segmentName;
+ StringRef sectionName;
+ SectionType sectionType;
+ SectionAttr sectionAttrs;
+ bool relocsToDefinedCanBeImplicit;
+
+ // Use same table used by when parsing .o files.
+ relocatableSectionInfoForContentType(type, segmentName, sectionName,
+ sectionType, sectionAttrs,
+ relocsToDefinedCanBeImplicit);
+ // If we already have a SectionInfo with this name, re-use it.
+ // This can happen if two ContentType map to the same mach-o section.
+ for (auto sect : _sectionMap) {
+ if (sect.second->sectionName.equals(sectionName) &&
+ sect.second->segmentName.equals(segmentName)) {
+ return sect.second;
+ }
+ }
+ // Otherwise allocate new SectionInfo object.
+ auto *sect = new (_allocator)
+ SectionInfo(segmentName, sectionName, sectionType, _ctx, sectionAttrs,
+ relocsToDefinedCanBeImplicit);
+ _sectionInfos.push_back(sect);
+ _sectionMap[type] = sect;
+ return sect;
+}
+
+#define ENTRY(seg, sect, type, atomType) \
+ {seg, sect, type, DefinedAtom::atomType }
+
+struct MachOFinalSectionFromAtomType {
+ StringRef segmentName;
+ StringRef sectionName;
+ SectionType sectionType;
+ DefinedAtom::ContentType atomType;
+};
+
+const MachOFinalSectionFromAtomType sectsToAtomType[] = {
+ ENTRY("__TEXT", "__text", S_REGULAR, typeCode),
+ ENTRY("__TEXT", "__text", S_REGULAR, typeMachHeader),
+ ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString),
+ ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String),
+ ENTRY("__TEXT", "__const", S_REGULAR, typeConstant),
+ ENTRY("__TEXT", "__const", S_4BYTE_LITERALS, typeLiteral4),
+ ENTRY("__TEXT", "__const", S_8BYTE_LITERALS, typeLiteral8),
+ ENTRY("__TEXT", "__const", S_16BYTE_LITERALS, typeLiteral16),
+ ENTRY("__TEXT", "__stubs", S_SYMBOL_STUBS, typeStub),
+ ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper),
+ ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA),
+ ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI),
+ ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo),
+ ENTRY("__DATA", "__data", S_REGULAR, typeData),
+ ENTRY("__DATA", "__const", S_REGULAR, typeConstData),
+ ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString),
+ ENTRY("__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS,
+ typeLazyPointer),
+ ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS,
+ typeInitializerPtr),
+ ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS,
+ typeTerminatorPtr),
+ ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS,
+ typeGOT),
+ ENTRY("__DATA", "__nl_symbol_ptr", S_NON_LAZY_SYMBOL_POINTERS,
+ typeNonLazyPointer),
+ ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES,
+ typeThunkTLV),
+ ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR,
+ typeTLVInitialData),
+ ENTRY("__DATA", "__thread_ptrs", S_THREAD_LOCAL_VARIABLE_POINTERS,
+ typeTLVInitializerPtr),
+ ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL,
+ typeTLVInitialZeroFill),
+ ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill),
+ ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples),
+};
+#undef ENTRY
+
+SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) {
+ for (auto &p : sectsToAtomType) {
+ if (p.atomType != atomType)
+ continue;
+ SectionAttr sectionAttrs = 0;
+ switch (atomType) {
+ case DefinedAtom::typeMachHeader:
+ case DefinedAtom::typeCode:
+ case DefinedAtom::typeStub:
+ case DefinedAtom::typeStubHelper:
+ sectionAttrs = S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS;
+ break;
+ case DefinedAtom::typeThunkTLV:
+ _hasTLVDescriptors = true;
+ break;
+ default:
+ break;
+ }
+ // If we already have a SectionInfo with this name, re-use it.
+ // This can happen if two ContentType map to the same mach-o section.
+ for (auto sect : _sectionMap) {
+ if (sect.second->sectionName.equals(p.sectionName) &&
+ sect.second->segmentName.equals(p.segmentName)) {
+ return sect.second;
+ }
+ }
+ // Otherwise allocate new SectionInfo object.
+ auto *sect = new (_allocator) SectionInfo(
+ p.segmentName, p.sectionName, p.sectionType, _ctx, sectionAttrs,
+ /* relocsToDefinedCanBeImplicit */ false);
+ _sectionInfos.push_back(sect);
+ _sectionMap[atomType] = sect;
+ return sect;
+ }
+ llvm_unreachable("content type not yet supported");
+}
+
+SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) {
+ if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
+ // Section for this atom is derived from content type.
+ DefinedAtom::ContentType type = atom->contentType();
+ auto pos = _sectionMap.find(type);
+ if ( pos != _sectionMap.end() )
+ return pos->second;
+ bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
+ return rMode ? getRelocatableSection(type) : getFinalSection(type);
+ } else {
+ // This atom needs to be in a custom section.
+ StringRef customName = atom->customSectionName();
+ // Look to see if we have already allocated the needed custom section.
+ for(SectionInfo *sect : _customSections) {
+ const DefinedAtom *firstAtom = sect->atomsAndOffsets.front().atom;
+ if (firstAtom->customSectionName().equals(customName)) {
+ return sect;
+ }
+ }
+ // Not found, so need to create a new custom section.
+ size_t seperatorIndex = customName.find('/');
+ assert(seperatorIndex != StringRef::npos);
+ StringRef segName = customName.slice(0, seperatorIndex);
+ StringRef sectName = customName.drop_front(seperatorIndex + 1);
+ auto *sect =
+ new (_allocator) SectionInfo(segName, sectName, S_REGULAR, _ctx,
+ 0, /* relocsToDefinedCanBeImplicit */ false);
+ _customSections.push_back(sect);
+ _sectionInfos.push_back(sect);
+ return sect;
+ }
+}
+
+void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) {
+ // Figure out offset for atom in this section given alignment constraints.
+ uint64_t offset = sect->size;
+ DefinedAtom::Alignment atomAlign = atom->alignment();
+ uint64_t align = atomAlign.value;
+ uint64_t requiredModulus = atomAlign.modulus;
+ uint64_t currentModulus = (offset % align);
+ if ( currentModulus != requiredModulus ) {
+ if ( requiredModulus > currentModulus )
+ offset += requiredModulus-currentModulus;
+ else
+ offset += align+requiredModulus-currentModulus;
+ }
+ // Record max alignment of any atom in this section.
+ if (align > sect->alignment)
+ sect->alignment = atomAlign.value;
+ // Assign atom to this section with this offset.
+ AtomInfo ai = {atom, offset};
+ sect->atomsAndOffsets.push_back(ai);
+ // Update section size to include this atom.
+ sect->size = offset + atom->size();
+}
+
+void Util::processDefinedAtoms(const lld::File &atomFile) {
+ for (const DefinedAtom *atom : atomFile.defined()) {
+ processAtomAttributes(atom);
+ assignAtomToSection(atom);
+ }
+}
+
+void Util::processAtomAttributes(const DefinedAtom *atom) {
+ if (auto *machoFile = dyn_cast<mach_o::MachOFile>(&atom->file())) {
+ // If the file doesn't use subsections via symbols, then make sure we don't
+ // add that flag to the final output file if we have a relocatable file.
+ if (!machoFile->subsectionsViaSymbols())
+ _subsectionsViaSymbols = false;
+
+ // All the source files must have min versions for us to output an object
+ // file with a min version.
+ if (auto v = machoFile->minVersion())
+ _minVersion = std::max(_minVersion, v);
+ else
+ _allSourceFilesHaveMinVersions = false;
+
+ // If we don't have a platform load command, but one of the source files
+ // does, then take the one from the file.
+ if (!_minVersionCommandType)
+ if (auto v = machoFile->minVersionLoadCommandKind())
+ _minVersionCommandType = v;
+ }
+}
+
+void Util::assignAtomToSection(const DefinedAtom *atom) {
+ if (atom->contentType() == DefinedAtom::typeMachHeader) {
+ _machHeaderAliasAtoms.push_back(atom);
+ // Assign atom to this section with this offset.
+ AtomInfo ai = {atom, 0};
+ sectionForAtom(atom)->atomsAndOffsets.push_back(ai);
+ } else if (atom->contentType() == DefinedAtom::typeDSOHandle)
+ _machHeaderAliasAtoms.push_back(atom);
+ else
+ appendAtom(sectionForAtom(atom), atom);
+}
+
+SegmentInfo *Util::segmentForName(StringRef segName) {
+ for (SegmentInfo *si : _segmentInfos) {
+ if ( si->name.equals(segName) )
+ return si;
+ }
+ auto *info = new (_allocator) SegmentInfo(segName);
+
+ // Set the initial segment protection.
+ if (segName.equals("__TEXT"))
+ info->init_access = VM_PROT_READ | VM_PROT_EXECUTE;
+ else if (segName.equals("__PAGEZERO"))
+ info->init_access = 0;
+ else if (segName.equals("__LINKEDIT"))
+ info->init_access = VM_PROT_READ;
+ else {
+ // All others default to read-write
+ info->init_access = VM_PROT_READ | VM_PROT_WRITE;
+ }
+
+ // Set max segment protection
+ // Note, its overkill to use a switch statement here, but makes it so much
+ // easier to use switch coverage to catch new cases.
+ switch (_ctx.os()) {
+ case lld::MachOLinkingContext::OS::unknown:
+ case lld::MachOLinkingContext::OS::macOSX:
+ case lld::MachOLinkingContext::OS::iOS_simulator:
+ if (segName.equals("__PAGEZERO")) {
+ info->max_access = 0;
+ break;
+ }
+ // All others default to all
+ info->max_access = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+ break;
+ case lld::MachOLinkingContext::OS::iOS:
+ // iPhoneOS always uses same protection for max and initial
+ info->max_access = info->init_access;
+ break;
+ }
+ _segmentInfos.push_back(info);
+ return info;
+}
+
+unsigned Util::SegmentSorter::weight(const SegmentInfo *seg) {
+ return llvm::StringSwitch<unsigned>(seg->name)
+ .Case("__PAGEZERO", 1)
+ .Case("__TEXT", 2)
+ .Case("__DATA", 3)
+ .Default(100);
+}
+
+bool Util::SegmentSorter::operator()(const SegmentInfo *left,
+ const SegmentInfo *right) {
+ return (weight(left) < weight(right));
+}
+
+unsigned Util::TextSectionSorter::weight(const SectionInfo *sect) {
+ return llvm::StringSwitch<unsigned>(sect->sectionName)
+ .Case("__text", 1)
+ .Case("__stubs", 2)
+ .Case("__stub_helper", 3)
+ .Case("__const", 4)
+ .Case("__cstring", 5)
+ .Case("__unwind_info", 98)
+ .Case("__eh_frame", 99)
+ .Default(10);
+}
+
+bool Util::TextSectionSorter::operator()(const SectionInfo *left,
+ const SectionInfo *right) {
+ return (weight(left) < weight(right));
+}
+
+void Util::organizeSections() {
+ // NOTE!: Keep this in sync with assignAddressesToSections.
+ switch (_ctx.outputMachOType()) {
+ case llvm::MachO::MH_EXECUTE:
+ // Main executables, need a zero-page segment
+ segmentForName("__PAGEZERO");
+ // Fall into next case.
+ LLVM_FALLTHROUGH;
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ // All dynamic code needs TEXT segment to hold the load commands.
+ segmentForName("__TEXT");
+ break;
+ default:
+ break;
+ }
+ segmentForName("__LINKEDIT");
+
+ // Group sections into segments.
+ for (SectionInfo *si : _sectionInfos) {
+ SegmentInfo *seg = segmentForName(si->segmentName);
+ seg->sections.push_back(si);
+ }
+ // Sort segments.
+ std::sort(_segmentInfos.begin(), _segmentInfos.end(), SegmentSorter());
+
+ // Sort sections within segments.
+ for (SegmentInfo *seg : _segmentInfos) {
+ if (seg->name.equals("__TEXT")) {
+ std::sort(seg->sections.begin(), seg->sections.end(),
+ TextSectionSorter());
+ }
+ }
+
+ // Record final section indexes.
+ uint32_t segmentIndex = 0;
+ uint32_t sectionIndex = 1;
+ for (SegmentInfo *seg : _segmentInfos) {
+ seg->normalizedSegmentIndex = segmentIndex++;
+ for (SectionInfo *sect : seg->sections)
+ sect->finalSectionIndex = sectionIndex++;
+ }
+}
+
+void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) {
+ seg->address = addr;
+ for (SectionInfo *sect : seg->sections) {
+ sect->address = llvm::alignTo(addr, sect->alignment);
+ addr = sect->address + sect->size;
+ }
+ seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize());
+}
+
+// __TEXT segment lays out backwards so padding is at front after load commands.
+void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg,
+ uint64_t &addr) {
+ seg->address = addr;
+ // Walks sections starting at end to calculate padding for start.
+ int64_t taddr = 0;
+ for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) {
+ SectionInfo *sect = *it;
+ taddr -= sect->size;
+ taddr = taddr & (0 - sect->alignment);
+ }
+ int64_t padding = taddr - hlcSize;
+ while (padding < 0)
+ padding += _ctx.pageSize();
+ // Start assigning section address starting at padded offset.
+ addr += (padding + hlcSize);
+ for (SectionInfo *sect : seg->sections) {
+ sect->address = llvm::alignTo(addr, sect->alignment);
+ addr = sect->address + sect->size;
+ }
+ seg->size = llvm::alignTo(addr - seg->address, _ctx.pageSize());
+}
+
+void Util::assignAddressesToSections(const NormalizedFile &file) {
+ // NOTE!: Keep this in sync with organizeSections.
+ size_t hlcSize = headerAndLoadCommandsSize(file,
+ _ctx.generateFunctionStartsLoadCommand());
+ uint64_t address = 0;
+ for (SegmentInfo *seg : _segmentInfos) {
+ if (seg->name.equals("__PAGEZERO")) {
+ seg->size = _ctx.pageZeroSize();
+ address += seg->size;
+ }
+ else if (seg->name.equals("__TEXT")) {
+ // _ctx.baseAddress() == 0 implies it was either unspecified or
+ // pageZeroSize is also 0. In either case resetting address is safe.
+ address = _ctx.baseAddress() ? _ctx.baseAddress() : address;
+ layoutSectionsInTextSegment(hlcSize, seg, address);
+ } else
+ layoutSectionsInSegment(seg, address);
+
+ address = llvm::alignTo(address, _ctx.pageSize());
+ }
+ DEBUG_WITH_TYPE("WriterMachO-norm",
+ llvm::dbgs() << "assignAddressesToSections()\n";
+ for (SegmentInfo *sgi : _segmentInfos) {
+ llvm::dbgs() << " address=" << llvm::format("0x%08llX", sgi->address)
+ << ", size=" << llvm::format("0x%08llX", sgi->size)
+ << ", segment-name='" << sgi->name
+ << "'\n";
+ for (SectionInfo *si : sgi->sections) {
+ llvm::dbgs()<< " addr=" << llvm::format("0x%08llX", si->address)
+ << ", size=" << llvm::format("0x%08llX", si->size)
+ << ", section-name='" << si->sectionName
+ << "\n";
+ }
+ }
+ );
+}
+
+void Util::copySegmentInfo(NormalizedFile &file) {
+ for (SegmentInfo *sgi : _segmentInfos) {
+ Segment seg;
+ seg.name = sgi->name;
+ seg.address = sgi->address;
+ seg.size = sgi->size;
+ seg.init_access = sgi->init_access;
+ seg.max_access = sgi->max_access;
+ file.segments.push_back(seg);
+ }
+}
+
+void Util::appendSection(SectionInfo *si, NormalizedFile &file) {
+ // Add new empty section to end of file.sections.
+ Section temp;
+ file.sections.push_back(std::move(temp));
+ Section* normSect = &file.sections.back();
+ // Copy fields to normalized section.
+ normSect->segmentName = si->segmentName;
+ normSect->sectionName = si->sectionName;
+ normSect->type = si->type;
+ normSect->attributes = si->attributes;
+ normSect->address = si->address;
+ normSect->alignment = si->alignment;
+ // Record where normalized section is.
+ si->normalizedSectionIndex = file.sections.size()-1;
+}
+
+void Util::copySectionContent(NormalizedFile &file) {
+ const bool r = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
+
+ // Utility function for ArchHandler to find address of atom in output file.
+ auto addrForAtom = [&] (const Atom &atom) -> uint64_t {
+ auto pos = _atomToAddress.find(&atom);
+ assert(pos != _atomToAddress.end());
+ return pos->second;
+ };
+
+ auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t {
+ for (const SectionInfo *sectInfo : _sectionInfos)
+ for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets)
+ if (atomInfo.atom == &atom)
+ return sectInfo->address;
+ llvm_unreachable("atom not assigned to section");
+ };
+
+ for (SectionInfo *si : _sectionInfos) {
+ Section *normSect = &file.sections[si->normalizedSectionIndex];
+ if (isZeroFillSection(si->type)) {
+ const uint8_t *empty = nullptr;
+ normSect->content = llvm::makeArrayRef(empty, si->size);
+ continue;
+ }
+ // Copy content from atoms to content buffer for section.
+ llvm::MutableArrayRef<uint8_t> sectionContent;
+ if (si->size) {
+ uint8_t *sectContent = file.ownedAllocations.Allocate<uint8_t>(si->size);
+ sectionContent = llvm::MutableArrayRef<uint8_t>(sectContent, si->size);
+ normSect->content = sectionContent;
+ }
+ for (AtomInfo &ai : si->atomsAndOffsets) {
+ if (!ai.atom->size()) {
+ assert(ai.atom->begin() == ai.atom->end() &&
+ "Cannot have references without content");
+ continue;
+ }
+ auto atomContent = sectionContent.slice(ai.offsetInSection,
+ ai.atom->size());
+ _archHandler.generateAtomContent(*ai.atom, r, addrForAtom,
+ sectionAddrForAtom, _ctx.baseAddress(),
+ atomContent);
+ }
+ }
+}
+
+void Util::copySectionInfo(NormalizedFile &file) {
+ file.sections.reserve(_sectionInfos.size());
+ // Write sections grouped by segment.
+ for (SegmentInfo *sgi : _segmentInfos) {
+ for (SectionInfo *si : sgi->sections) {
+ appendSection(si, file);
+ }
+ }
+}
+
+void Util::updateSectionInfo(NormalizedFile &file) {
+ file.sections.reserve(_sectionInfos.size());
+ // sections grouped by segment.
+ for (SegmentInfo *sgi : _segmentInfos) {
+ Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex];
+ normSeg->address = sgi->address;
+ normSeg->size = sgi->size;
+ for (SectionInfo *si : sgi->sections) {
+ Section *normSect = &file.sections[si->normalizedSectionIndex];
+ normSect->address = si->address;
+ }
+ }
+}
+
+void Util::copyEntryPointAddress(NormalizedFile &nFile) {
+ if (!_entryAtom) {
+ nFile.entryAddress = 0;
+ return;
+ }
+
+ if (_ctx.outputTypeHasEntry()) {
+ if (_archHandler.isThumbFunction(*_entryAtom))
+ nFile.entryAddress = (_atomToAddress[_entryAtom] | 1);
+ else
+ nFile.entryAddress = _atomToAddress[_entryAtom];
+ }
+}
+
+void Util::buildAtomToAddressMap() {
+ DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
+ << "assign atom addresses:\n");
+ const bool lookForEntry = _ctx.outputTypeHasEntry();
+ for (SectionInfo *sect : _sectionInfos) {
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ _atomToAddress[info.atom] = sect->address + info.offsetInSection;
+ if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) &&
+ (info.atom->size() != 0) &&
+ info.atom->name() == _ctx.entrySymbolName()) {
+ _entryAtom = info.atom;
+ }
+ DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
+ << " address="
+ << llvm::format("0x%016X", _atomToAddress[info.atom])
+ << llvm::format(" 0x%09lX", info.atom)
+ << ", file=#"
+ << info.atom->file().ordinal()
+ << ", atom=#"
+ << info.atom->ordinal()
+ << ", name="
+ << info.atom->name()
+ << ", type="
+ << info.atom->contentType()
+ << "\n");
+ }
+ }
+ DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
+ << "assign header alias atom addresses:\n");
+ for (const Atom *atom : _machHeaderAliasAtoms) {
+ _atomToAddress[atom] = _ctx.baseAddress();
+#ifndef NDEBUG
+ if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) {
+ DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
+ << " address="
+ << llvm::format("0x%016X", _atomToAddress[atom])
+ << llvm::format(" 0x%09lX", atom)
+ << ", file=#"
+ << definedAtom->file().ordinal()
+ << ", atom=#"
+ << definedAtom->ordinal()
+ << ", name="
+ << definedAtom->name()
+ << ", type="
+ << definedAtom->contentType()
+ << "\n");
+ } else {
+ DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs()
+ << " address="
+ << llvm::format("0x%016X", _atomToAddress[atom])
+ << " atom=" << atom
+ << " name=" << atom->name() << "\n");
+ }
+#endif
+ }
+}
+
+llvm::Error Util::synthesizeDebugNotes(NormalizedFile &file) {
+
+ // Bail out early if we don't need to generate a debug map.
+ if (_ctx.debugInfoMode() == MachOLinkingContext::DebugInfoMode::noDebugMap)
+ return llvm::Error::success();
+
+ std::vector<const DefinedAtom*> atomsNeedingDebugNotes;
+ std::set<const mach_o::MachOFile*> filesWithStabs;
+ bool objFileHasDwarf = false;
+ const File *objFile = nullptr;
+
+ for (SectionInfo *sect : _sectionInfos) {
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ if (const DefinedAtom *atom = dyn_cast<DefinedAtom>(info.atom)) {
+
+ // FIXME: No stabs/debug-notes for symbols that wouldn't be in the
+ // symbol table.
+ // FIXME: No stabs/debug-notes for kernel dtrace probes.
+
+ if (atom->contentType() == DefinedAtom::typeCFI ||
+ atom->contentType() == DefinedAtom::typeCString)
+ continue;
+
+ // Whenever we encounter a new file, update the 'objfileHasDwarf' flag.
+ if (&info.atom->file() != objFile) {
+ objFileHasDwarf = false;
+ if (const mach_o::MachOFile *atomFile =
+ dyn_cast<mach_o::MachOFile>(&info.atom->file())) {
+ if (atomFile->debugInfo()) {
+ if (isa<mach_o::DwarfDebugInfo>(atomFile->debugInfo()))
+ objFileHasDwarf = true;
+ else if (isa<mach_o::StabsDebugInfo>(atomFile->debugInfo()))
+ filesWithStabs.insert(atomFile);
+ }
+ }
+ }
+
+ // If this atom is from a file that needs dwarf, add it to the list.
+ if (objFileHasDwarf)
+ atomsNeedingDebugNotes.push_back(info.atom);
+ }
+ }
+ }
+
+ // Sort atoms needing debug notes by file ordinal, then atom ordinal.
+ std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(),
+ [](const DefinedAtom *lhs, const DefinedAtom *rhs) {
+ if (lhs->file().ordinal() != rhs->file().ordinal())
+ return (lhs->file().ordinal() < rhs->file().ordinal());
+ return (lhs->ordinal() < rhs->ordinal());
+ });
+
+ // FIXME: Handle <rdar://problem/17689030>: Add -add_ast_path option to \
+ // linker which add N_AST stab entry to output
+ // See OutputFile::synthesizeDebugNotes in ObjectFile.cpp in ld64.
+
+ StringRef oldFileName = "";
+ StringRef oldDirPath = "";
+ bool wroteStartSO = false;
+ std::unordered_set<std::string> seenFiles;
+ for (const DefinedAtom *atom : atomsNeedingDebugNotes) {
+ const auto &atomFile = cast<mach_o::MachOFile>(atom->file());
+ assert(dyn_cast_or_null<lld::mach_o::DwarfDebugInfo>(atomFile.debugInfo())
+ && "file for atom needing debug notes does not contain dwarf");
+ auto &dwarf = cast<lld::mach_o::DwarfDebugInfo>(*atomFile.debugInfo());
+
+ auto &tu = dwarf.translationUnitSource();
+ StringRef newFileName = tu.name;
+ StringRef newDirPath = tu.path;
+
+ // Add an SO whenever the TU source file changes.
+ if (newFileName != oldFileName || newDirPath != oldDirPath) {
+ // Translation unit change, emit ending SO
+ if (oldFileName != "")
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
+
+ oldFileName = newFileName;
+ oldDirPath = newDirPath;
+
+ // If newDirPath doesn't end with a '/' we need to add one:
+ if (newDirPath.back() != '/') {
+ char *p =
+ file.ownedAllocations.Allocate<char>(newDirPath.size() + 2);
+ memcpy(p, newDirPath.data(), newDirPath.size());
+ p[newDirPath.size()] = '/';
+ p[newDirPath.size() + 1] = '\0';
+ newDirPath = p;
+ }
+
+ // New translation unit, emit start SOs:
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newDirPath));
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newFileName));
+
+ // Synthesize OSO for start of file.
+ char *fullPath = nullptr;
+ {
+ SmallString<1024> pathBuf(atomFile.path());
+ if (auto EC = llvm::sys::fs::make_absolute(pathBuf))
+ return llvm::errorCodeToError(EC);
+ fullPath = file.ownedAllocations.Allocate<char>(pathBuf.size() + 1);
+ memcpy(fullPath, pathBuf.c_str(), pathBuf.size() + 1);
+ }
+
+ // Get mod time.
+ uint32_t modTime = 0;
+ llvm::sys::fs::file_status stat;
+ if (!llvm::sys::fs::status(fullPath, stat))
+ if (llvm::sys::fs::exists(stat))
+ modTime = llvm::sys::toTimeT(stat.getLastModificationTime());
+
+ _stabs.push_back(mach_o::Stab(nullptr, N_OSO, _ctx.getCPUSubType(), 1,
+ modTime, fullPath));
+ // <rdar://problem/6337329> linker should put cpusubtype in n_sect field
+ // of nlist entry for N_OSO debug note entries.
+ wroteStartSO = true;
+ }
+
+ if (atom->contentType() == DefinedAtom::typeCode) {
+ // Synthesize BNSYM and start FUN stabs.
+ _stabs.push_back(mach_o::Stab(atom, N_BNSYM, 1, 0, 0, ""));
+ _stabs.push_back(mach_o::Stab(atom, N_FUN, 1, 0, 0, atom->name()));
+ // Synthesize any SOL stabs needed
+ // FIXME: add SOL stabs.
+ _stabs.push_back(mach_o::Stab(nullptr, N_FUN, 0, 0,
+ atom->rawContent().size(), ""));
+ _stabs.push_back(mach_o::Stab(nullptr, N_ENSYM, 1, 0,
+ atom->rawContent().size(), ""));
+ } else {
+ if (atom->scope() == Atom::scopeTranslationUnit)
+ _stabs.push_back(mach_o::Stab(atom, N_STSYM, 1, 0, 0, atom->name()));
+ else
+ _stabs.push_back(mach_o::Stab(nullptr, N_GSYM, 1, 0, 0, atom->name()));
+ }
+ }
+
+ // Emit ending SO if necessary.
+ if (wroteStartSO)
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
+
+ // Copy any stabs from .o file.
+ for (const auto *objFile : filesWithStabs) {
+ const auto &stabsList =
+ cast<mach_o::StabsDebugInfo>(objFile->debugInfo())->stabs();
+ for (auto &stab : stabsList) {
+ // FIXME: Drop stabs whose atoms have been dead-stripped.
+ _stabs.push_back(stab);
+ }
+ }
+
+ return llvm::Error::success();
+}
+
+uint16_t Util::descBits(const DefinedAtom* atom) {
+ uint16_t desc = 0;
+ switch (atom->merge()) {
+ case lld::DefinedAtom::mergeNo:
+ case lld::DefinedAtom::mergeAsTentative:
+ break;
+ case lld::DefinedAtom::mergeAsWeak:
+ case lld::DefinedAtom::mergeAsWeakAndAddressUsed:
+ desc |= N_WEAK_DEF;
+ break;
+ case lld::DefinedAtom::mergeSameNameAndSize:
+ case lld::DefinedAtom::mergeByLargestSection:
+ case lld::DefinedAtom::mergeByContent:
+ llvm_unreachable("Unsupported DefinedAtom::merge()");
+ break;
+ }
+ if (atom->contentType() == lld::DefinedAtom::typeResolver)
+ desc |= N_SYMBOL_RESOLVER;
+ if (atom->contentType() == lld::DefinedAtom::typeMachHeader)
+ desc |= REFERENCED_DYNAMICALLY;
+ if (_archHandler.isThumbFunction(*atom))
+ desc |= N_ARM_THUMB_DEF;
+ if (atom->deadStrip() == DefinedAtom::deadStripNever &&
+ _ctx.outputMachOType() == llvm::MachO::MH_OBJECT) {
+ if ((atom->contentType() != DefinedAtom::typeInitializerPtr)
+ && (atom->contentType() != DefinedAtom::typeTerminatorPtr))
+ desc |= N_NO_DEAD_STRIP;
+ }
+ return desc;
+}
+
+bool Util::AtomSorter::operator()(const AtomAndIndex &left,
+ const AtomAndIndex &right) {
+ return (left.atom->name().compare(right.atom->name()) < 0);
+}
+
+llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom,
+ bool &inGlobalsRegion,
+ SymbolScope &scope) {
+ bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
+ switch (atom->scope()) {
+ case Atom::scopeTranslationUnit:
+ scope = 0;
+ inGlobalsRegion = false;
+ return llvm::Error::success();
+ case Atom::scopeLinkageUnit:
+ if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::exported) &&
+ _ctx.exportSymbolNamed(atom->name())) {
+ return llvm::make_error<GenericError>(
+ Twine("cannot export hidden symbol ") + atom->name());
+ }
+ if (rMode) {
+ if (_ctx.keepPrivateExterns()) {
+ // -keep_private_externs means keep in globals region as N_PEXT.
+ scope = N_PEXT | N_EXT;
+ inGlobalsRegion = true;
+ return llvm::Error::success();
+ }
+ }
+ // scopeLinkageUnit symbols are no longer global once linked.
+ scope = N_PEXT;
+ inGlobalsRegion = false;
+ return llvm::Error::success();
+ case Atom::scopeGlobal:
+ if (_ctx.exportRestrictMode()) {
+ if (_ctx.exportSymbolNamed(atom->name())) {
+ scope = N_EXT;
+ inGlobalsRegion = true;
+ return llvm::Error::success();
+ } else {
+ scope = N_PEXT;
+ inGlobalsRegion = false;
+ return llvm::Error::success();
+ }
+ } else {
+ scope = N_EXT;
+ inGlobalsRegion = true;
+ return llvm::Error::success();
+ }
+ break;
+ }
+ llvm_unreachable("atom->scope() unknown enum value");
+}
+
+
+
+llvm::Error Util::addSymbols(const lld::File &atomFile,
+ NormalizedFile &file) {
+ bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
+ // Mach-O symbol table has four regions: stabs, locals, globals, undefs.
+
+ // Add all stabs.
+ for (auto &stab : _stabs) {
+ lld::mach_o::normalized::Symbol sym;
+ sym.type = static_cast<NListType>(stab.type);
+ sym.scope = 0;
+ sym.sect = stab.other;
+ sym.desc = stab.desc;
+ if (stab.atom)
+ sym.value = _atomToAddress[stab.atom];
+ else
+ sym.value = stab.value;
+ sym.name = stab.str;
+ file.stabsSymbols.push_back(sym);
+ }
+
+ // Add all local (non-global) symbols in address order
+ std::vector<AtomAndIndex> globals;
+ globals.reserve(512);
+ for (SectionInfo *sect : _sectionInfos) {
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ const DefinedAtom *atom = info.atom;
+ if (!atom->name().empty()) {
+ SymbolScope symbolScope;
+ bool inGlobalsRegion;
+ if (auto ec = getSymbolTableRegion(atom, inGlobalsRegion, symbolScope)){
+ return ec;
+ }
+ if (inGlobalsRegion) {
+ AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope };
+ globals.push_back(ai);
+ } else {
+ lld::mach_o::normalized::Symbol sym;
+ sym.name = atom->name();
+ sym.type = N_SECT;
+ sym.scope = symbolScope;
+ sym.sect = sect->finalSectionIndex;
+ sym.desc = descBits(atom);
+ sym.value = _atomToAddress[atom];
+ _atomToSymbolIndex[atom] = file.localSymbols.size();
+ file.localSymbols.push_back(sym);
+ }
+ } else if (rMode && _archHandler.needsLocalSymbolInRelocatableFile(atom)){
+ // Create 'Lxxx' labels for anonymous atoms if archHandler says so.
+ static unsigned tempNum = 1;
+ char tmpName[16];
+ sprintf(tmpName, "L%04u", tempNum++);
+ StringRef tempRef(tmpName);
+ lld::mach_o::normalized::Symbol sym;
+ sym.name = tempRef.copy(file.ownedAllocations);
+ sym.type = N_SECT;
+ sym.scope = 0;
+ sym.sect = sect->finalSectionIndex;
+ sym.desc = 0;
+ sym.value = _atomToAddress[atom];
+ _atomToSymbolIndex[atom] = file.localSymbols.size();
+ file.localSymbols.push_back(sym);
+ }
+ }
+ }
+
+ // Sort global symbol alphabetically, then add to symbol table.
+ std::sort(globals.begin(), globals.end(), AtomSorter());
+ const uint32_t globalStartIndex = file.localSymbols.size();
+ for (AtomAndIndex &ai : globals) {
+ lld::mach_o::normalized::Symbol sym;
+ sym.name = ai.atom->name();
+ sym.type = N_SECT;
+ sym.scope = ai.scope;
+ sym.sect = ai.index;
+ sym.desc = descBits(static_cast<const DefinedAtom*>(ai.atom));
+ sym.value = _atomToAddress[ai.atom];
+ _atomToSymbolIndex[ai.atom] = globalStartIndex + file.globalSymbols.size();
+ file.globalSymbols.push_back(sym);
+ }
+
+ // Sort undefined symbol alphabetically, then add to symbol table.
+ std::vector<AtomAndIndex> undefs;
+ undefs.reserve(128);
+ for (const UndefinedAtom *atom : atomFile.undefined()) {
+ AtomAndIndex ai = { atom, 0, N_EXT };
+ undefs.push_back(ai);
+ }
+ for (const SharedLibraryAtom *atom : atomFile.sharedLibrary()) {
+ AtomAndIndex ai = { atom, 0, N_EXT };
+ undefs.push_back(ai);
+ }
+ std::sort(undefs.begin(), undefs.end(), AtomSorter());
+ const uint32_t start = file.globalSymbols.size() + file.localSymbols.size();
+ for (AtomAndIndex &ai : undefs) {
+ lld::mach_o::normalized::Symbol sym;
+ uint16_t desc = 0;
+ if (!rMode) {
+ uint8_t ordinal = 0;
+ if (!_ctx.useFlatNamespace())
+ ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom));
+ llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal);
+ }
+ sym.name = ai.atom->name();
+ sym.type = N_UNDF;
+ sym.scope = ai.scope;
+ sym.sect = 0;
+ sym.desc = desc;
+ sym.value = 0;
+ _atomToSymbolIndex[ai.atom] = file.undefinedSymbols.size() + start;
+ file.undefinedSymbols.push_back(sym);
+ }
+
+ return llvm::Error::success();
+}
+
+const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) {
+ for (const Reference *ref : *lpAtom) {
+ if (_archHandler.isLazyPointer(*ref)) {
+ return ref->target();
+ }
+ }
+ return nullptr;
+}
+
+const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) {
+ for (const Reference *ref : *stubAtom) {
+ if (const Atom *ta = ref->target()) {
+ if (const DefinedAtom *lpAtom = dyn_cast<DefinedAtom>(ta)) {
+ const Atom *target = targetOfLazyPointer(lpAtom);
+ if (target)
+ return target;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) {
+ for (SectionInfo *si : _sectionInfos) {
+ Section &normSect = file.sections[si->normalizedSectionIndex];
+ switch (si->type) {
+ case llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS:
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ bool foundTarget = false;
+ for (const Reference *ref : *info.atom) {
+ const Atom *target = ref->target();
+ if (target) {
+ if (isa<const SharedLibraryAtom>(target)) {
+ uint32_t index = _atomToSymbolIndex[target];
+ normSect.indirectSymbols.push_back(index);
+ foundTarget = true;
+ } else {
+ normSect.indirectSymbols.push_back(
+ llvm::MachO::INDIRECT_SYMBOL_LOCAL);
+ }
+ }
+ }
+ if (!foundTarget) {
+ normSect.indirectSymbols.push_back(
+ llvm::MachO::INDIRECT_SYMBOL_ABS);
+ }
+ }
+ break;
+ case llvm::MachO::S_LAZY_SYMBOL_POINTERS:
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ const Atom *target = targetOfLazyPointer(info.atom);
+ if (target) {
+ uint32_t index = _atomToSymbolIndex[target];
+ normSect.indirectSymbols.push_back(index);
+ }
+ }
+ break;
+ case llvm::MachO::S_SYMBOL_STUBS:
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ const Atom *target = targetOfStub(info.atom);
+ if (target) {
+ uint32_t index = _atomToSymbolIndex[target];
+ normSect.indirectSymbols.push_back(index);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Util::addDependentDylibs(const lld::File &atomFile,
+ NormalizedFile &nFile) {
+ // Scan all imported symbols and build up list of dylibs they are from.
+ int ordinal = 1;
+ for (const auto *dylib : _ctx.allDylibs()) {
+ DylibPathToInfo::iterator pos = _dylibInfo.find(dylib->installName());
+ if (pos == _dylibInfo.end()) {
+ DylibInfo info;
+ bool flatNamespaceAtom = dylib == _ctx.flatNamespaceFile();
+
+ // If we're in -flat_namespace mode (or this atom came from the flat
+ // namespace file under -undefined dynamic_lookup) then use the flat
+ // lookup ordinal.
+ if (flatNamespaceAtom || _ctx.useFlatNamespace())
+ info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+ else
+ info.ordinal = ordinal++;
+ info.hasWeak = false;
+ info.hasNonWeak = !info.hasWeak;
+ _dylibInfo[dylib->installName()] = info;
+
+ // Unless this was a flat_namespace atom, record the source dylib.
+ if (!flatNamespaceAtom) {
+ DependentDylib depInfo;
+ depInfo.path = dylib->installName();
+ depInfo.kind = llvm::MachO::LC_LOAD_DYLIB;
+ depInfo.currentVersion = _ctx.dylibCurrentVersion(dylib->path());
+ depInfo.compatVersion = _ctx.dylibCompatVersion(dylib->path());
+ nFile.dependentDylibs.push_back(depInfo);
+ }
+ } else {
+ pos->second.hasWeak = false;
+ pos->second.hasNonWeak = !pos->second.hasWeak;
+ }
+ }
+ // Automatically weak link dylib in which all symbols are weak (canBeNull).
+ for (DependentDylib &dep : nFile.dependentDylibs) {
+ DylibInfo &info = _dylibInfo[dep.path];
+ if (info.hasWeak && !info.hasNonWeak)
+ dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB;
+ else if (_ctx.isUpwardDylib(dep.path))
+ dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB;
+ }
+}
+
+int Util::dylibOrdinal(const SharedLibraryAtom *sa) {
+ return _dylibInfo[sa->loadName()].ordinal;
+}
+
+void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex,
+ uint64_t &segmentStartAddr) {
+ segmentIndex = 0;
+ for (const SegmentInfo *seg : _segmentInfos) {
+ if ((seg->address <= sect->address)
+ && (seg->address+seg->size >= sect->address+sect->size)) {
+ segmentStartAddr = seg->address;
+ return;
+ }
+ ++segmentIndex;
+ }
+ llvm_unreachable("section not in any segment");
+}
+
+uint32_t Util::sectionIndexForAtom(const Atom *atom) {
+ uint64_t address = _atomToAddress[atom];
+ for (const SectionInfo *si : _sectionInfos) {
+ if ((si->address <= address) && (address < si->address+si->size))
+ return si->finalSectionIndex;
+ }
+ llvm_unreachable("atom not in any section");
+}
+
+void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) {
+ if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
+ return;
+
+ // Utility function for ArchHandler to find symbol index for an atom.
+ auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t {
+ auto pos = _atomToSymbolIndex.find(&atom);
+ assert(pos != _atomToSymbolIndex.end());
+ return pos->second;
+ };
+
+ // Utility function for ArchHandler to find section index for an atom.
+ auto sectIndexForAtom = [&] (const Atom &atom) -> uint32_t {
+ return sectionIndexForAtom(&atom);
+ };
+
+ // Utility function for ArchHandler to find address of atom in output file.
+ auto addressForAtom = [&] (const Atom &atom) -> uint64_t {
+ auto pos = _atomToAddress.find(&atom);
+ assert(pos != _atomToAddress.end());
+ return pos->second;
+ };
+
+ for (SectionInfo *si : _sectionInfos) {
+ Section &normSect = file.sections[si->normalizedSectionIndex];
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ const DefinedAtom *atom = info.atom;
+ for (const Reference *ref : *atom) {
+ // Skip emitting relocs for sections which are always able to be
+ // implicitly regenerated and where the relocation targets an address
+ // which is defined.
+ if (si->relocsToDefinedCanBeImplicit && isa<DefinedAtom>(ref->target()))
+ continue;
+ _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref,
+ symIndexForAtom,
+ sectIndexForAtom,
+ addressForAtom,
+ normSect.relocations);
+ }
+ }
+ }
+}
+
+void Util::addFunctionStarts(const lld::File &, NormalizedFile &file) {
+ if (!_ctx.generateFunctionStartsLoadCommand())
+ return;
+ file.functionStarts.reserve(8192);
+ // Delta compress function starts, starting with the mach header symbol.
+ const uint64_t badAddress = ~0ULL;
+ uint64_t addr = badAddress;
+ for (SectionInfo *si : _sectionInfos) {
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ auto type = info.atom->contentType();
+ if (type == DefinedAtom::typeMachHeader) {
+ addr = _atomToAddress[info.atom];
+ continue;
+ }
+ if (type != DefinedAtom::typeCode)
+ continue;
+ assert(addr != badAddress && "Missing mach header symbol");
+ // Skip atoms which have 0 size. This is so that LC_FUNCTION_STARTS
+ // can't spill in to the next section.
+ if (!info.atom->size())
+ continue;
+ uint64_t nextAddr = _atomToAddress[info.atom];
+ if (_archHandler.isThumbFunction(*info.atom))
+ nextAddr |= 1;
+ uint64_t delta = nextAddr - addr;
+ if (delta) {
+ ByteBuffer buffer;
+ buffer.append_uleb128(delta);
+ file.functionStarts.insert(file.functionStarts.end(), buffer.bytes(),
+ buffer.bytes() + buffer.size());
+ }
+ addr = nextAddr;
+ }
+ }
+
+ // Null terminate, and pad to pointer size for this arch.
+ file.functionStarts.push_back(0);
+
+ auto size = file.functionStarts.size();
+ for (unsigned i = size, e = llvm::alignTo(size, _ctx.is64Bit() ? 8 : 4);
+ i != e; ++i)
+ file.functionStarts.push_back(0);
+}
+
+void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) {
+ if (!_ctx.generateDataInCodeLoadCommand())
+ return;
+ for (SectionInfo *si : _sectionInfos) {
+ for (const AtomInfo &info : si->atomsAndOffsets) {
+ // Atoms that contain data-in-code have "transition" references
+ // which mark a point where the embedded data starts of ends.
+ // This needs to be converted to the mach-o format which is an array
+ // of data-in-code ranges.
+ uint32_t startOffset = 0;
+ DataRegionType mode = DataRegionType(0);
+ for (const Reference *ref : *info.atom) {
+ if (ref->kindNamespace() != Reference::KindNamespace::mach_o)
+ continue;
+ if (_archHandler.isDataInCodeTransition(ref->kindValue())) {
+ DataRegionType nextMode = (DataRegionType)ref->addend();
+ if (mode != nextMode) {
+ if (mode != 0) {
+ // Found end data range, so make range entry.
+ DataInCode entry;
+ entry.offset = si->address + info.offsetInSection + startOffset;
+ entry.length = ref->offsetInAtom() - startOffset;
+ entry.kind = mode;
+ file.dataInCode.push_back(entry);
+ }
+ }
+ mode = nextMode;
+ startOffset = ref->offsetInAtom();
+ }
+ }
+ if (mode != 0) {
+ // Function ends with data (no end transition).
+ DataInCode entry;
+ entry.offset = si->address + info.offsetInSection + startOffset;
+ entry.length = info.atom->size() - startOffset;
+ entry.kind = mode;
+ file.dataInCode.push_back(entry);
+ }
+ }
+ }
+}
+
+void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
+ NormalizedFile &nFile) {
+ if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT)
+ return;
+
+ uint8_t segmentIndex;
+ uint64_t segmentStartAddr;
+ uint32_t offsetInBindInfo = 0;
+
+ for (SectionInfo *sect : _sectionInfos) {
+ segIndexForSection(sect, segmentIndex, segmentStartAddr);
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ const DefinedAtom *atom = info.atom;
+ for (const Reference *ref : *atom) {
+ uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom()
+ - segmentStartAddr;
+ const Atom* targ = ref->target();
+ if (_archHandler.isPointer(*ref)) {
+ // A pointer to a DefinedAtom requires rebasing.
+ if (isa<DefinedAtom>(targ)) {
+ RebaseLocation rebase;
+ rebase.segIndex = segmentIndex;
+ rebase.segOffset = segmentOffset;
+ rebase.kind = llvm::MachO::REBASE_TYPE_POINTER;
+ nFile.rebasingInfo.push_back(rebase);
+ }
+ // A pointer to an SharedLibraryAtom requires binding.
+ if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) {
+ BindLocation bind;
+ bind.segIndex = segmentIndex;
+ bind.segOffset = segmentOffset;
+ bind.kind = llvm::MachO::BIND_TYPE_POINTER;
+ bind.canBeNull = sa->canBeNullAtRuntime();
+ bind.ordinal = dylibOrdinal(sa);
+ bind.symbolName = targ->name();
+ bind.addend = ref->addend();
+ nFile.bindingInfo.push_back(bind);
+ }
+ }
+ else if (_archHandler.isLazyPointer(*ref)) {
+ BindLocation bind;
+ if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) {
+ bind.ordinal = dylibOrdinal(sa);
+ } else {
+ bind.ordinal = llvm::MachO::BIND_SPECIAL_DYLIB_SELF;
+ }
+ bind.segIndex = segmentIndex;
+ bind.segOffset = segmentOffset;
+ bind.kind = llvm::MachO::BIND_TYPE_POINTER;
+ bind.canBeNull = false; //sa->canBeNullAtRuntime();
+ bind.symbolName = targ->name();
+ bind.addend = ref->addend();
+ nFile.lazyBindingInfo.push_back(bind);
+
+ // Now that we know the segmentOffset and the ordinal attribute,
+ // we can fix the helper's code
+
+ fixLazyReferenceImm(atom, offsetInBindInfo, nFile);
+
+ // 5 bytes for opcodes + variable sizes (target name + \0 and offset
+ // encode's size)
+ offsetInBindInfo +=
+ 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset);
+ if (bind.ordinal > BIND_IMMEDIATE_MASK)
+ offsetInBindInfo += llvm::getULEB128Size(bind.ordinal);
+ }
+ }
+ }
+ }
+}
+
+void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
+ NormalizedFile &file) {
+ for (const Reference *ref : *atom) {
+ const DefinedAtom *da = dyn_cast<DefinedAtom>(ref->target());
+ if (da == nullptr)
+ return;
+
+ const Reference *helperRef = nullptr;
+ for (const Reference *hr : *da) {
+ if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) {
+ helperRef = hr;
+ break;
+ }
+ }
+ if (helperRef == nullptr)
+ continue;
+
+ // TODO: maybe get the fixed atom content from _archHandler ?
+ for (SectionInfo *sectInfo : _sectionInfos) {
+ for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) {
+ if (atomInfo.atom == helperRef->target()) {
+ auto sectionContent =
+ file.sections[sectInfo->normalizedSectionIndex].content;
+ uint8_t *rawb =
+ file.ownedAllocations.Allocate<uint8_t>(sectionContent.size());
+ llvm::MutableArrayRef<uint8_t> newContent{rawb,
+ sectionContent.size()};
+ std::copy(sectionContent.begin(), sectionContent.end(),
+ newContent.begin());
+ llvm::support::ulittle32_t *loc =
+ reinterpret_cast<llvm::support::ulittle32_t *>(
+ &newContent[atomInfo.offsetInSection +
+ helperRef->offsetInAtom()]);
+ *loc = offset;
+ file.sections[sectInfo->normalizedSectionIndex].content = newContent;
+ }
+ }
+ }
+ }
+}
+
+void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) {
+ if (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT)
+ return;
+
+ for (SectionInfo *sect : _sectionInfos) {
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ const DefinedAtom *atom = info.atom;
+ if (atom->scope() != Atom::scopeGlobal)
+ continue;
+ if (_ctx.exportRestrictMode()) {
+ if (!_ctx.exportSymbolNamed(atom->name()))
+ continue;
+ }
+ Export exprt;
+ exprt.name = atom->name();
+ exprt.offset = _atomToAddress[atom] - _ctx.baseAddress();
+ exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
+ if (atom->merge() == DefinedAtom::mergeAsWeak)
+ exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
+ else
+ exprt.flags = 0;
+ exprt.otherOffset = 0;
+ exprt.otherName = StringRef();
+ nFile.exportInfo.push_back(exprt);
+ }
+ }
+}
+
+uint32_t Util::fileFlags() {
+ // FIXME: these need to determined at runtime.
+ if (_ctx.outputMachOType() == MH_OBJECT) {
+ return _subsectionsViaSymbols ? (uint32_t)MH_SUBSECTIONS_VIA_SYMBOLS : 0;
+ } else {
+ uint32_t flags = MH_DYLDLINK;
+ if (!_ctx.useFlatNamespace())
+ flags |= MH_TWOLEVEL | MH_NOUNDEFS;
+ if ((_ctx.outputMachOType() == MH_EXECUTE) && _ctx.PIE())
+ flags |= MH_PIE;
+ if (_hasTLVDescriptors)
+ flags |= (MH_PIE | MH_HAS_TLV_DESCRIPTORS);
+ return flags;
+ }
+}
+
+} // end anonymous namespace
+
+namespace lld {
+namespace mach_o {
+namespace normalized {
+
+/// Convert a set of Atoms into a normalized mach-o file.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+normalizedFromAtoms(const lld::File &atomFile,
+ const MachOLinkingContext &context) {
+ // The util object buffers info until the normalized file can be made.
+ Util util(context);
+ util.processDefinedAtoms(atomFile);
+ util.organizeSections();
+
+ std::unique_ptr<NormalizedFile> f(new NormalizedFile());
+ NormalizedFile &normFile = *f.get();
+ normFile.arch = context.arch();
+ normFile.fileType = context.outputMachOType();
+ normFile.flags = util.fileFlags();
+ normFile.stackSize = context.stackSize();
+ normFile.installName = context.installName();
+ normFile.currentVersion = context.currentVersion();
+ normFile.compatVersion = context.compatibilityVersion();
+ normFile.os = context.os();
+
+ // If we are emitting an object file, then the min version is the maximum
+ // of the min's of all the source files and the cmdline.
+ if (normFile.fileType == llvm::MachO::MH_OBJECT)
+ normFile.minOSverson = std::max(context.osMinVersion(), util.minVersion());
+ else
+ normFile.minOSverson = context.osMinVersion();
+
+ normFile.minOSVersionKind = util.minVersionCommandType();
+
+ normFile.sdkVersion = context.sdkVersion();
+ normFile.sourceVersion = context.sourceVersion();
+
+ if (context.generateVersionLoadCommand() &&
+ context.os() != MachOLinkingContext::OS::unknown)
+ normFile.hasMinVersionLoadCommand = true;
+ else if (normFile.fileType == llvm::MachO::MH_OBJECT &&
+ util.allSourceFilesHaveMinVersions() &&
+ ((normFile.os != MachOLinkingContext::OS::unknown) ||
+ util.minVersionCommandType())) {
+ // If we emit an object file, then it should contain a min version load
+ // command if all of the source files also contained min version commands.
+ // Also, we either need to have a platform, or found a platform from the
+ // source object files.
+ normFile.hasMinVersionLoadCommand = true;
+ }
+ normFile.generateDataInCodeLoadCommand =
+ context.generateDataInCodeLoadCommand();
+ normFile.pageSize = context.pageSize();
+ normFile.rpaths = context.rpaths();
+ util.addDependentDylibs(atomFile, normFile);
+ util.copySegmentInfo(normFile);
+ util.copySectionInfo(normFile);
+ util.assignAddressesToSections(normFile);
+ util.buildAtomToAddressMap();
+ if (auto err = util.synthesizeDebugNotes(normFile))
+ return std::move(err);
+ util.updateSectionInfo(normFile);
+ util.copySectionContent(normFile);
+ if (auto ec = util.addSymbols(atomFile, normFile)) {
+ return std::move(ec);
+ }
+ util.addIndirectSymbols(atomFile, normFile);
+ util.addRebaseAndBindingInfo(atomFile, normFile);
+ util.addExportInfo(atomFile, normFile);
+ util.addSectionRelocs(atomFile, normFile);
+ util.addFunctionStarts(atomFile, normFile);
+ util.buildDataInCodeArray(atomFile, normFile);
+ util.copyEntryPointAddress(normFile);
+
+ return std::move(f);
+}
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file Converts from in-memory normalized mach-o to in-memory Atoms.
+///
+/// +------------+
+/// | normalized |
+/// +------------+
+/// |
+/// |
+/// v
+/// +-------+
+/// | Atoms |
+/// +-------+
+
+#include "ArchHandler.h"
+#include "Atoms.h"
+#include "File.h"
+#include "MachONormalizedFile.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+#define DEBUG_TYPE "normalized-file-to-atoms"
+
+namespace lld {
+namespace mach_o {
+
+
+namespace { // anonymous
+
+
+#define ENTRY(seg, sect, type, atomType) \
+ {seg, sect, type, DefinedAtom::atomType }
+
+struct MachORelocatableSectionToAtomType {
+ StringRef segmentName;
+ StringRef sectionName;
+ SectionType sectionType;
+ DefinedAtom::ContentType atomType;
+};
+
+const MachORelocatableSectionToAtomType sectsToAtomType[] = {
+ ENTRY("__TEXT", "__text", S_REGULAR, typeCode),
+ ENTRY("__TEXT", "__text", S_REGULAR, typeResolver),
+ ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString),
+ ENTRY("", "", S_CSTRING_LITERALS, typeCString),
+ ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String),
+ ENTRY("__TEXT", "__const", S_REGULAR, typeConstant),
+ ENTRY("__TEXT", "__const_coal", S_COALESCED, typeConstant),
+ ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI),
+ ENTRY("__TEXT", "__eh_frame", S_REGULAR, typeCFI),
+ ENTRY("__TEXT", "__literal4", S_4BYTE_LITERALS, typeLiteral4),
+ ENTRY("__TEXT", "__literal8", S_8BYTE_LITERALS, typeLiteral8),
+ ENTRY("__TEXT", "__literal16", S_16BYTE_LITERALS, typeLiteral16),
+ ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA),
+ ENTRY("__DATA", "__data", S_REGULAR, typeData),
+ ENTRY("__DATA", "__datacoal_nt", S_COALESCED, typeData),
+ ENTRY("__DATA", "__const", S_REGULAR, typeConstData),
+ ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString),
+ ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS,
+ typeInitializerPtr),
+ ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS,
+ typeTerminatorPtr),
+ ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS,
+ typeGOT),
+ ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill),
+ ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS,
+ typeGOT),
+ ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples),
+ ENTRY("__DATA", "__thread_vars", S_THREAD_LOCAL_VARIABLES,
+ typeThunkTLV),
+ ENTRY("__DATA", "__thread_data", S_THREAD_LOCAL_REGULAR, typeTLVInitialData),
+ ENTRY("__DATA", "__thread_bss", S_THREAD_LOCAL_ZEROFILL,
+ typeTLVInitialZeroFill),
+ ENTRY("__DATA", "__objc_imageinfo", S_REGULAR, typeObjCImageInfo),
+ ENTRY("__DATA", "__objc_catlist", S_REGULAR, typeObjC2CategoryList),
+ ENTRY("", "", S_INTERPOSING, typeInterposingTuples),
+ ENTRY("__LD", "__compact_unwind", S_REGULAR,
+ typeCompactUnwindInfo),
+ ENTRY("", "", S_REGULAR, typeUnknown)
+};
+#undef ENTRY
+
+
+/// Figures out ContentType of a mach-o section.
+DefinedAtom::ContentType atomTypeFromSection(const Section §ion,
+ bool &customSectionName) {
+ // First look for match of name and type. Empty names in table are wildcards.
+ customSectionName = false;
+ for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ;
+ p->atomType != DefinedAtom::typeUnknown; ++p) {
+ if (p->sectionType != section.type)
+ continue;
+ if (!p->segmentName.equals(section.segmentName) && !p->segmentName.empty())
+ continue;
+ if (!p->sectionName.equals(section.sectionName) && !p->sectionName.empty())
+ continue;
+ customSectionName = p->segmentName.empty() && p->sectionName.empty();
+ return p->atomType;
+ }
+ // Look for code denoted by section attributes
+ if (section.attributes & S_ATTR_PURE_INSTRUCTIONS)
+ return DefinedAtom::typeCode;
+
+ return DefinedAtom::typeUnknown;
+}
+
+enum AtomizeModel {
+ atomizeAtSymbols,
+ atomizeFixedSize,
+ atomizePointerSize,
+ atomizeUTF8,
+ atomizeUTF16,
+ atomizeCFI,
+ atomizeCU,
+ atomizeCFString
+};
+
+/// Returns info on how to atomize a section of the specified ContentType.
+void sectionParseInfo(DefinedAtom::ContentType atomType,
+ unsigned int &sizeMultiple,
+ DefinedAtom::Scope &scope,
+ DefinedAtom::Merge &merge,
+ AtomizeModel &atomizeModel) {
+ struct ParseInfo {
+ DefinedAtom::ContentType atomType;
+ unsigned int sizeMultiple;
+ DefinedAtom::Scope scope;
+ DefinedAtom::Merge merge;
+ AtomizeModel atomizeModel;
+ };
+
+ #define ENTRY(type, size, scope, merge, model) \
+ {DefinedAtom::type, size, DefinedAtom::scope, DefinedAtom::merge, model }
+
+ static const ParseInfo parseInfo[] = {
+ ENTRY(typeCode, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols),
+ ENTRY(typeData, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols),
+ ENTRY(typeConstData, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols),
+ ENTRY(typeZeroFill, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols),
+ ENTRY(typeConstant, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols),
+ ENTRY(typeCString, 1, scopeLinkageUnit, mergeByContent,
+ atomizeUTF8),
+ ENTRY(typeUTF16String, 1, scopeLinkageUnit, mergeByContent,
+ atomizeUTF16),
+ ENTRY(typeCFI, 4, scopeTranslationUnit, mergeNo,
+ atomizeCFI),
+ ENTRY(typeLiteral4, 4, scopeLinkageUnit, mergeByContent,
+ atomizeFixedSize),
+ ENTRY(typeLiteral8, 8, scopeLinkageUnit, mergeByContent,
+ atomizeFixedSize),
+ ENTRY(typeLiteral16, 16, scopeLinkageUnit, mergeByContent,
+ atomizeFixedSize),
+ ENTRY(typeCFString, 4, scopeLinkageUnit, mergeByContent,
+ atomizeCFString),
+ ENTRY(typeInitializerPtr, 4, scopeTranslationUnit, mergeNo,
+ atomizePointerSize),
+ ENTRY(typeTerminatorPtr, 4, scopeTranslationUnit, mergeNo,
+ atomizePointerSize),
+ ENTRY(typeCompactUnwindInfo, 4, scopeTranslationUnit, mergeNo,
+ atomizeCU),
+ ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent,
+ atomizePointerSize),
+ ENTRY(typeObjC2CategoryList, 4, scopeTranslationUnit, mergeByContent,
+ atomizePointerSize),
+ ENTRY(typeUnknown, 1, scopeGlobal, mergeNo,
+ atomizeAtSymbols)
+ };
+ #undef ENTRY
+ const int tableLen = sizeof(parseInfo) / sizeof(ParseInfo);
+ for (int i=0; i < tableLen; ++i) {
+ if (parseInfo[i].atomType == atomType) {
+ sizeMultiple = parseInfo[i].sizeMultiple;
+ scope = parseInfo[i].scope;
+ merge = parseInfo[i].merge;
+ atomizeModel = parseInfo[i].atomizeModel;
+ return;
+ }
+ }
+
+ // Unknown type is atomized by symbols.
+ sizeMultiple = 1;
+ scope = DefinedAtom::scopeGlobal;
+ merge = DefinedAtom::mergeNo;
+ atomizeModel = atomizeAtSymbols;
+}
+
+
+Atom::Scope atomScope(uint8_t scope) {
+ switch (scope) {
+ case N_EXT:
+ return Atom::scopeGlobal;
+ case N_PEXT:
+ case N_PEXT | N_EXT:
+ return Atom::scopeLinkageUnit;
+ case 0:
+ return Atom::scopeTranslationUnit;
+ }
+ llvm_unreachable("unknown scope value!");
+}
+
+void appendSymbolsInSection(
+ const std::vector<lld::mach_o::normalized::Symbol> &inSymbols,
+ uint32_t sectionIndex,
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) {
+ for (const lld::mach_o::normalized::Symbol &sym : inSymbols) {
+ // Only look at definition symbols.
+ if ((sym.type & N_TYPE) != N_SECT)
+ continue;
+ if (sym.sect != sectionIndex)
+ continue;
+ outSyms.push_back(&sym);
+ }
+}
+
+void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion,
+ MachOFile &file, uint64_t symbolAddr, StringRef symbolName,
+ uint16_t symbolDescFlags, Atom::Scope symbolScope,
+ uint64_t nextSymbolAddr, bool scatterable, bool copyRefs) {
+ // Mach-O symbol table does have size in it. Instead the size is the
+ // difference between this and the next symbol.
+ uint64_t size = nextSymbolAddr - symbolAddr;
+ uint64_t offset = symbolAddr - section.address;
+ bool noDeadStrip = (symbolDescFlags & N_NO_DEAD_STRIP) || !scatterable;
+ if (isZeroFillSection(section.type)) {
+ file.addZeroFillDefinedAtom(symbolName, symbolScope, offset, size,
+ noDeadStrip, copyRefs, §ion);
+ } else {
+ DefinedAtom::Merge merge = (symbolDescFlags & N_WEAK_DEF)
+ ? DefinedAtom::mergeAsWeak : DefinedAtom::mergeNo;
+ bool thumb = (symbolDescFlags & N_ARM_THUMB_DEF);
+ if (atomType == DefinedAtom::typeUnknown) {
+ // Mach-O needs a segment and section name. Concatenate those two
+ // with a / separator (e.g. "seg/sect") to fit into the lld model
+ // of just a section name.
+ std::string segSectName = section.segmentName.str()
+ + "/" + section.sectionName.str();
+ file.addDefinedAtomInCustomSection(symbolName, symbolScope, atomType,
+ merge, thumb, noDeadStrip, offset,
+ size, segSectName, true, §ion);
+ } else {
+ if ((atomType == lld::DefinedAtom::typeCode) &&
+ (symbolDescFlags & N_SYMBOL_RESOLVER)) {
+ atomType = lld::DefinedAtom::typeResolver;
+ }
+ file.addDefinedAtom(symbolName, symbolScope, atomType, merge,
+ offset, size, thumb, noDeadStrip, copyRefs, §ion);
+ }
+ }
+}
+
+llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
+ const Section §ion,
+ const NormalizedFile &normalizedFile,
+ MachOFile &file, bool scatterable,
+ bool copyRefs) {
+ // Find section's index.
+ uint32_t sectIndex = 1;
+ for (auto § : normalizedFile.sections) {
+ if (§ == §ion)
+ break;
+ ++sectIndex;
+ }
+
+ // Find all symbols in this section.
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols;
+ appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols);
+ appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols);
+
+ // Sort symbols.
+ std::sort(symbols.begin(), symbols.end(),
+ [](const lld::mach_o::normalized::Symbol *lhs,
+ const lld::mach_o::normalized::Symbol *rhs) -> bool {
+ if (lhs == rhs)
+ return false;
+ // First by address.
+ uint64_t lhsAddr = lhs->value;
+ uint64_t rhsAddr = rhs->value;
+ if (lhsAddr != rhsAddr)
+ return lhsAddr < rhsAddr;
+ // If same address, one is an alias so sort by scope.
+ Atom::Scope lScope = atomScope(lhs->scope);
+ Atom::Scope rScope = atomScope(rhs->scope);
+ if (lScope != rScope)
+ return lScope < rScope;
+ // If same address and scope, see if one might be better as
+ // the alias.
+ bool lPrivate = (lhs->name.front() == 'l');
+ bool rPrivate = (rhs->name.front() == 'l');
+ if (lPrivate != rPrivate)
+ return lPrivate;
+ // If same address and scope, sort by name.
+ return lhs->name < rhs->name;
+ });
+
+ // Debug logging of symbols.
+ // for (const Symbol *sym : symbols)
+ // llvm::errs() << " sym: "
+ // << llvm::format("0x%08llx ", (uint64_t)sym->value)
+ // << ", " << sym->name << "\n";
+
+ // If section has no symbols and no content, there are no atoms.
+ if (symbols.empty() && section.content.empty())
+ return llvm::Error::success();
+
+ if (symbols.empty()) {
+ // Section has no symbols, put all content in one anonymous atom.
+ atomFromSymbol(atomType, section, file, section.address, StringRef(),
+ 0, Atom::scopeTranslationUnit,
+ section.address + section.content.size(),
+ scatterable, copyRefs);
+ }
+ else if (symbols.front()->value != section.address) {
+ // Section has anonymous content before first symbol.
+ atomFromSymbol(atomType, section, file, section.address, StringRef(),
+ 0, Atom::scopeTranslationUnit, symbols.front()->value,
+ scatterable, copyRefs);
+ }
+
+ const lld::mach_o::normalized::Symbol *lastSym = nullptr;
+ for (const lld::mach_o::normalized::Symbol *sym : symbols) {
+ if (lastSym != nullptr) {
+ // Ignore any assembler added "ltmpNNN" symbol at start of section
+ // if there is another symbol at the start.
+ if ((lastSym->value != sym->value)
+ || lastSym->value != section.address
+ || !lastSym->name.startswith("ltmp")) {
+ atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
+ lastSym->desc, atomScope(lastSym->scope), sym->value,
+ scatterable, copyRefs);
+ }
+ }
+ lastSym = sym;
+ }
+ if (lastSym != nullptr) {
+ atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name,
+ lastSym->desc, atomScope(lastSym->scope),
+ section.address + section.content.size(),
+ scatterable, copyRefs);
+ }
+
+ // If object built without .subsections_via_symbols, add reference chain.
+ if (!scatterable) {
+ MachODefinedAtom *prevAtom = nullptr;
+ file.eachAtomInSection(section,
+ [&](MachODefinedAtom *atom, uint64_t offset)->void {
+ if (prevAtom)
+ prevAtom->addReference(Reference::KindNamespace::all,
+ Reference::KindArch::all,
+ Reference::kindLayoutAfter, 0, atom, 0);
+ prevAtom = atom;
+ });
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error processSection(DefinedAtom::ContentType atomType,
+ const Section §ion,
+ bool customSectionName,
+ const NormalizedFile &normalizedFile,
+ MachOFile &file, bool scatterable,
+ bool copyRefs) {
+ const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+
+ // Get info on how to atomize section.
+ unsigned int sizeMultiple;
+ DefinedAtom::Scope scope;
+ DefinedAtom::Merge merge;
+ AtomizeModel atomizeModel;
+ sectionParseInfo(atomType, sizeMultiple, scope, merge, atomizeModel);
+
+ // Validate section size.
+ if ((section.content.size() % sizeMultiple) != 0)
+ return llvm::make_error<GenericError>(Twine("Section ")
+ + section.segmentName
+ + "/" + section.sectionName
+ + " has size ("
+ + Twine(section.content.size())
+ + ") which is not a multiple of "
+ + Twine(sizeMultiple));
+
+ if (atomizeModel == atomizeAtSymbols) {
+ // Break section up into atoms each with a fixed size.
+ return processSymboledSection(atomType, section, normalizedFile, file,
+ scatterable, copyRefs);
+ } else {
+ unsigned int size;
+ for (unsigned int offset = 0, e = section.content.size(); offset != e;) {
+ switch (atomizeModel) {
+ case atomizeFixedSize:
+ // Break section up into atoms each with a fixed size.
+ size = sizeMultiple;
+ break;
+ case atomizePointerSize:
+ // Break section up into atoms each the size of a pointer.
+ size = is64 ? 8 : 4;
+ break;
+ case atomizeUTF8:
+ // Break section up into zero terminated c-strings.
+ size = 0;
+ for (unsigned int i = offset; i < e; ++i) {
+ if (section.content[i] == 0) {
+ size = i + 1 - offset;
+ break;
+ }
+ }
+ break;
+ case atomizeUTF16:
+ // Break section up into zero terminated UTF16 strings.
+ size = 0;
+ for (unsigned int i = offset; i < e; i += 2) {
+ if ((section.content[i] == 0) && (section.content[i + 1] == 0)) {
+ size = i + 2 - offset;
+ break;
+ }
+ }
+ break;
+ case atomizeCFI:
+ // Break section up into dwarf unwind CFIs (FDE or CIE).
+ size = read32(§ion.content[offset], isBig) + 4;
+ if (offset+size > section.content.size()) {
+ return llvm::make_error<GenericError>(Twine("Section ")
+ + section.segmentName
+ + "/" + section.sectionName
+ + " is malformed. Size of CFI "
+ "starting at offset ("
+ + Twine(offset)
+ + ") is past end of section.");
+ }
+ break;
+ case atomizeCU:
+ // Break section up into compact unwind entries.
+ size = is64 ? 32 : 20;
+ break;
+ case atomizeCFString:
+ // Break section up into NS/CFString objects.
+ size = is64 ? 32 : 16;
+ break;
+ case atomizeAtSymbols:
+ break;
+ }
+ if (size == 0) {
+ return llvm::make_error<GenericError>(Twine("Section ")
+ + section.segmentName
+ + "/" + section.sectionName
+ + " is malformed. The last atom "
+ "is not zero terminated.");
+ }
+ if (customSectionName) {
+ // Mach-O needs a segment and section name. Concatenate those two
+ // with a / separator (e.g. "seg/sect") to fit into the lld model
+ // of just a section name.
+ std::string segSectName = section.segmentName.str()
+ + "/" + section.sectionName.str();
+ file.addDefinedAtomInCustomSection(StringRef(), scope, atomType,
+ merge, false, false, offset,
+ size, segSectName, true, §ion);
+ } else {
+ file.addDefinedAtom(StringRef(), scope, atomType, merge, offset, size,
+ false, false, copyRefs, §ion);
+ }
+ offset += size;
+ }
+ }
+ return llvm::Error::success();
+}
+
+const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile,
+ uint64_t address) {
+ for (const Section &s : normalizedFile.sections) {
+ uint64_t sAddr = s.address;
+ if ((sAddr <= address) && (address < sAddr+s.content.size())) {
+ return &s;
+ }
+ }
+ return nullptr;
+}
+
+const MachODefinedAtom *
+findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file,
+ uint64_t addr, Reference::Addend &addend) {
+ const Section *sect = nullptr;
+ sect = findSectionCoveringAddress(normalizedFile, addr);
+ if (!sect)
+ return nullptr;
+
+ uint32_t offsetInTarget;
+ uint64_t offsetInSect = addr - sect->address;
+ auto atom =
+ file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
+ addend = offsetInTarget;
+ return atom;
+}
+
+// Walks all relocations for a section in a normalized .o file and
+// creates corresponding lld::Reference objects.
+llvm::Error convertRelocs(const Section §ion,
+ const NormalizedFile &normalizedFile,
+ bool scatterable,
+ MachOFile &file,
+ ArchHandler &handler) {
+ // Utility function for ArchHandler to find atom by its address.
+ auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr,
+ const lld::Atom **atom, Reference::Addend *addend)
+ -> llvm::Error {
+ if (sectIndex > normalizedFile.sections.size())
+ return llvm::make_error<GenericError>(Twine("out of range section "
+ "index (") + Twine(sectIndex) + ")");
+ const Section *sect = nullptr;
+ if (sectIndex == 0) {
+ sect = findSectionCoveringAddress(normalizedFile, addr);
+ if (!sect)
+ return llvm::make_error<GenericError>(Twine("address (" + Twine(addr)
+ + ") is not in any section"));
+ } else {
+ sect = &normalizedFile.sections[sectIndex-1];
+ }
+ uint32_t offsetInTarget;
+ uint64_t offsetInSect = addr - sect->address;
+ *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
+ *addend = offsetInTarget;
+ return llvm::Error::success();
+ };
+
+ // Utility function for ArchHandler to find atom by its symbol index.
+ auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
+ -> llvm::Error {
+ // Find symbol from index.
+ const lld::mach_o::normalized::Symbol *sym = nullptr;
+ uint32_t numStabs = normalizedFile.stabsSymbols.size();
+ uint32_t numLocal = normalizedFile.localSymbols.size();
+ uint32_t numGlobal = normalizedFile.globalSymbols.size();
+ uint32_t numUndef = normalizedFile.undefinedSymbols.size();
+ assert(symbolIndex >= numStabs && "Searched for stab via atomBySymbol?");
+ if (symbolIndex < numStabs+numLocal) {
+ sym = &normalizedFile.localSymbols[symbolIndex-numStabs];
+ } else if (symbolIndex < numStabs+numLocal+numGlobal) {
+ sym = &normalizedFile.globalSymbols[symbolIndex-numStabs-numLocal];
+ } else if (symbolIndex < numStabs+numLocal+numGlobal+numUndef) {
+ sym = &normalizedFile.undefinedSymbols[symbolIndex-numStabs-numLocal-
+ numGlobal];
+ } else {
+ return llvm::make_error<GenericError>(Twine("symbol index (")
+ + Twine(symbolIndex) + ") out of range");
+ }
+
+ // Find atom from symbol.
+ if ((sym->type & N_TYPE) == N_SECT) {
+ if (sym->sect > normalizedFile.sections.size())
+ return llvm::make_error<GenericError>(Twine("symbol section index (")
+ + Twine(sym->sect) + ") out of range ");
+ const Section &symSection = normalizedFile.sections[sym->sect-1];
+ uint64_t targetOffsetInSect = sym->value - symSection.address;
+ MachODefinedAtom *target = file.findAtomCoveringAddress(symSection,
+ targetOffsetInSect);
+ if (target) {
+ *result = target;
+ return llvm::Error::success();
+ }
+ return llvm::make_error<GenericError>("no atom found for defined symbol");
+ } else if ((sym->type & N_TYPE) == N_UNDF) {
+ const lld::Atom *target = file.findUndefAtom(sym->name);
+ if (target) {
+ *result = target;
+ return llvm::Error::success();
+ }
+ return llvm::make_error<GenericError>("no undefined atom found for sym");
+ } else {
+ // Search undefs
+ return llvm::make_error<GenericError>("no atom found for symbol");
+ }
+ };
+
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ // Use old-school iterator so that paired relocations can be grouped.
+ for (auto it=section.relocations.begin(), e=section.relocations.end();
+ it != e; ++it) {
+ const Relocation &reloc = *it;
+ // Find atom this relocation is in.
+ if (reloc.offset > section.content.size())
+ return llvm::make_error<GenericError>(
+ Twine("r_address (") + Twine(reloc.offset)
+ + ") is larger than section size ("
+ + Twine(section.content.size()) + ")");
+ uint32_t offsetInAtom;
+ MachODefinedAtom *inAtom = file.findAtomCoveringAddress(section,
+ reloc.offset,
+ &offsetInAtom);
+ assert(inAtom && "r_address in range, should have found atom");
+ uint64_t fixupAddress = section.address + reloc.offset;
+
+ const lld::Atom *target = nullptr;
+ Reference::Addend addend = 0;
+ Reference::KindValue kind;
+ if (handler.isPairedReloc(reloc)) {
+ // Handle paired relocations together.
+ const Relocation &reloc2 = *++it;
+ auto relocErr = handler.getPairReferenceInfo(
+ reloc, reloc2, inAtom, offsetInAtom, fixupAddress, isBig, scatterable,
+ atomByAddr, atomBySymbol, &kind, &target, &addend);
+ if (relocErr) {
+ return handleErrors(std::move(relocErr),
+ [&](std::unique_ptr<GenericError> GE) {
+ return llvm::make_error<GenericError>(
+ Twine("bad relocation (") + GE->getMessage()
+ + ") in section "
+ + section.segmentName + "/" + section.sectionName
+ + " (r1_address=" + Twine::utohexstr(reloc.offset)
+ + ", r1_type=" + Twine(reloc.type)
+ + ", r1_extern=" + Twine(reloc.isExtern)
+ + ", r1_length=" + Twine((int)reloc.length)
+ + ", r1_pcrel=" + Twine(reloc.pcRel)
+ + (!reloc.scattered ? (Twine(", r1_symbolnum=")
+ + Twine(reloc.symbol))
+ : (Twine(", r1_scattered=1, r1_value=")
+ + Twine(reloc.value)))
+ + ")"
+ + ", (r2_address=" + Twine::utohexstr(reloc2.offset)
+ + ", r2_type=" + Twine(reloc2.type)
+ + ", r2_extern=" + Twine(reloc2.isExtern)
+ + ", r2_length=" + Twine((int)reloc2.length)
+ + ", r2_pcrel=" + Twine(reloc2.pcRel)
+ + (!reloc2.scattered ? (Twine(", r2_symbolnum=")
+ + Twine(reloc2.symbol))
+ : (Twine(", r2_scattered=1, r2_value=")
+ + Twine(reloc2.value)))
+ + ")" );
+ });
+ }
+ }
+ else {
+ // Use ArchHandler to convert relocation record into information
+ // needed to instantiate an lld::Reference object.
+ auto relocErr = handler.getReferenceInfo(
+ reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr,
+ atomBySymbol, &kind, &target, &addend);
+ if (relocErr) {
+ return handleErrors(std::move(relocErr),
+ [&](std::unique_ptr<GenericError> GE) {
+ return llvm::make_error<GenericError>(
+ Twine("bad relocation (") + GE->getMessage()
+ + ") in section "
+ + section.segmentName + "/" + section.sectionName
+ + " (r_address=" + Twine::utohexstr(reloc.offset)
+ + ", r_type=" + Twine(reloc.type)
+ + ", r_extern=" + Twine(reloc.isExtern)
+ + ", r_length=" + Twine((int)reloc.length)
+ + ", r_pcrel=" + Twine(reloc.pcRel)
+ + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol))
+ : (Twine(", r_scattered=1, r_value=")
+ + Twine(reloc.value)))
+ + ")" );
+ });
+ }
+ }
+ // Instantiate an lld::Reference object and add to its atom.
+ inAtom->addReference(Reference::KindNamespace::mach_o,
+ handler.kindArch(),
+ kind, offsetInAtom, target, addend);
+ }
+
+ return llvm::Error::success();
+}
+
+bool isDebugInfoSection(const Section §ion) {
+ if ((section.attributes & S_ATTR_DEBUG) == 0)
+ return false;
+ return section.segmentName.equals("__DWARF");
+}
+
+static const Atom* findDefinedAtomByName(MachOFile &file, Twine name) {
+ std::string strName = name.str();
+ for (auto *atom : file.defined())
+ if (atom->name() == strName)
+ return atom;
+ return nullptr;
+}
+
+static StringRef copyDebugString(StringRef str, BumpPtrAllocator &alloc) {
+ char *strCopy = alloc.Allocate<char>(str.size() + 1);
+ memcpy(strCopy, str.data(), str.size());
+ strCopy[str.size()] = '\0';
+ return strCopy;
+}
+
+llvm::Error parseStabs(MachOFile &file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs) {
+
+ if (normalizedFile.stabsSymbols.empty())
+ return llvm::Error::success();
+
+ // FIXME: Kill this off when we can move to sane yaml parsing.
+ std::unique_ptr<BumpPtrAllocator> allocator;
+ if (copyRefs)
+ allocator = std::make_unique<BumpPtrAllocator>();
+
+ enum { start, inBeginEnd } state = start;
+
+ const Atom *currentAtom = nullptr;
+ uint64_t currentAtomAddress = 0;
+ StabsDebugInfo::StabsList stabsList;
+ for (const auto &stabSym : normalizedFile.stabsSymbols) {
+ Stab stab(nullptr, stabSym.type, stabSym.sect, stabSym.desc,
+ stabSym.value, stabSym.name);
+ switch (state) {
+ case start:
+ switch (static_cast<StabType>(stabSym.type)) {
+ case N_BNSYM:
+ state = inBeginEnd;
+ currentAtomAddress = stabSym.value;
+ Reference::Addend addend;
+ currentAtom = findAtomCoveringAddress(normalizedFile, file,
+ currentAtomAddress, addend);
+ if (addend != 0)
+ return llvm::make_error<GenericError>(
+ "Non-zero addend for BNSYM '" + stabSym.name + "' in " +
+ file.path());
+ if (currentAtom)
+ stab.atom = currentAtom;
+ else {
+ // FIXME: ld64 just issues a warning here - should we match that?
+ return llvm::make_error<GenericError>(
+ "can't find atom for stabs BNSYM at " +
+ Twine::utohexstr(stabSym.value) + " in " + file.path());
+ }
+ break;
+ case N_SO:
+ case N_OSO:
+ // Not associated with an atom, just copy.
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ break;
+ case N_GSYM: {
+ auto colonIdx = stabSym.name.find(':');
+ if (colonIdx != StringRef::npos) {
+ StringRef name = stabSym.name.substr(0, colonIdx);
+ currentAtom = findDefinedAtomByName(file, "_" + name);
+ stab.atom = currentAtom;
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ } else {
+ currentAtom = findDefinedAtomByName(file, stabSym.name);
+ stab.atom = currentAtom;
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ }
+ if (stab.atom == nullptr)
+ return llvm::make_error<GenericError>(
+ "can't find atom for N_GSYM stabs" + stabSym.name +
+ " in " + file.path());
+ break;
+ }
+ case N_FUN:
+ return llvm::make_error<GenericError>(
+ "old-style N_FUN stab '" + stabSym.name + "' unsupported");
+ default:
+ return llvm::make_error<GenericError>(
+ "unrecognized stab symbol '" + stabSym.name + "'");
+ }
+ break;
+ case inBeginEnd:
+ stab.atom = currentAtom;
+ switch (static_cast<StabType>(stabSym.type)) {
+ case N_ENSYM:
+ state = start;
+ currentAtom = nullptr;
+ break;
+ case N_FUN:
+ // Just copy the string.
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ break;
+ default:
+ return llvm::make_error<GenericError>(
+ "unrecognized stab symbol '" + stabSym.name + "'");
+ }
+ }
+ llvm::dbgs() << "Adding to stabsList: " << stab << "\n";
+ stabsList.push_back(stab);
+ }
+
+ file.setDebugInfo(std::make_unique<StabsDebugInfo>(std::move(stabsList)));
+
+ // FIXME: Kill this off when we fix YAML memory ownership.
+ file.debugInfo()->setAllocator(std::move(allocator));
+
+ return llvm::Error::success();
+}
+
+static llvm::DataExtractor
+dataExtractorFromSection(const NormalizedFile &normalizedFile,
+ const Section &S) {
+ const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ StringRef SecData(reinterpret_cast<const char*>(S.content.data()),
+ S.content.size());
+ return llvm::DataExtractor(SecData, !isBig, is64 ? 8 : 4);
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
+ uint64_t abbrCode) {
+ uint64_t offset = 0;
+ while (abbrevData.getULEB128(&offset) != abbrCode) {
+ // Tag
+ abbrevData.getULEB128(&offset);
+ // DW_CHILDREN
+ abbrevData.getU8(&offset);
+ // Attributes
+ while (abbrevData.getULEB128(&offset) | abbrevData.getULEB128(&offset))
+ ;
+ }
+ return offset;
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static Expected<const char *>
+getIndexedString(const NormalizedFile &normalizedFile,
+ llvm::dwarf::Form form, llvm::DataExtractor infoData,
+ uint64_t &infoOffset, const Section &stringsSection) {
+ if (form == llvm::dwarf::DW_FORM_string)
+ return infoData.getCStr(&infoOffset);
+ if (form != llvm::dwarf::DW_FORM_strp)
+ return llvm::make_error<GenericError>(
+ "string field encoded without DW_FORM_strp");
+ uint64_t stringOffset = infoData.getU32(&infoOffset);
+ llvm::DataExtractor stringsData =
+ dataExtractorFromSection(normalizedFile, stringsSection);
+ return stringsData.getCStr(&stringOffset);
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static llvm::Expected<TranslationUnitSource>
+readCompUnit(const NormalizedFile &normalizedFile,
+ const Section &info,
+ const Section &abbrev,
+ const Section &strings,
+ StringRef path) {
+ // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+ // inspection" code if possible.
+ uint64_t offset = 0;
+ llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
+ auto infoData = dataExtractorFromSection(normalizedFile, info);
+ uint32_t length = infoData.getU32(&offset);
+ if (length == llvm::dwarf::DW_LENGTH_DWARF64) {
+ Format = llvm::dwarf::DwarfFormat::DWARF64;
+ infoData.getU64(&offset);
+ }
+ else if (length >= llvm::dwarf::DW_LENGTH_lo_reserved)
+ return llvm::make_error<GenericError>("Malformed DWARF in " + path);
+
+ uint16_t version = infoData.getU16(&offset);
+
+ if (version < 2 || version > 4)
+ return llvm::make_error<GenericError>("Unsupported DWARF version in " +
+ path);
+
+ infoData.getU32(&offset); // Abbrev offset (should be zero)
+ uint8_t addrSize = infoData.getU8(&offset);
+
+ uint32_t abbrCode = infoData.getULEB128(&offset);
+ auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
+ uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
+ uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
+ if (tag != llvm::dwarf::DW_TAG_compile_unit)
+ return llvm::make_error<GenericError>("top level DIE is not a compile unit");
+ // DW_CHILDREN
+ abbrevData.getU8(&abbrevOffset);
+ uint32_t name;
+ llvm::dwarf::Form form;
+ llvm::dwarf::FormParams formParams = {version, addrSize, Format};
+ TranslationUnitSource tu;
+ while ((name = abbrevData.getULEB128(&abbrevOffset)) |
+ (form = static_cast<llvm::dwarf::Form>(
+ abbrevData.getULEB128(&abbrevOffset))) &&
+ (name != 0 || form != 0)) {
+ switch (name) {
+ case llvm::dwarf::DW_AT_name: {
+ if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
+ strings))
+ tu.name = *eName;
+ else
+ return eName.takeError();
+ break;
+ }
+ case llvm::dwarf::DW_AT_comp_dir: {
+ if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
+ strings))
+ tu.path = *eName;
+ else
+ return eName.takeError();
+ break;
+ }
+ default:
+ llvm::DWARFFormValue::skipValue(form, infoData, &offset, formParams);
+ }
+ }
+ return tu;
+}
+
+llvm::Error parseDebugInfo(MachOFile &file,
+ const NormalizedFile &normalizedFile, bool copyRefs) {
+
+ // Find the interesting debug info sections.
+ const Section *debugInfo = nullptr;
+ const Section *debugAbbrev = nullptr;
+ const Section *debugStrings = nullptr;
+
+ for (auto &s : normalizedFile.sections) {
+ if (s.segmentName == "__DWARF") {
+ if (s.sectionName == "__debug_info")
+ debugInfo = &s;
+ else if (s.sectionName == "__debug_abbrev")
+ debugAbbrev = &s;
+ else if (s.sectionName == "__debug_str")
+ debugStrings = &s;
+ }
+ }
+
+ if (!debugInfo)
+ return parseStabs(file, normalizedFile, copyRefs);
+
+ if (debugInfo->content.size() == 0)
+ return llvm::Error::success();
+
+ if (debugInfo->content.size() < 12)
+ return llvm::make_error<GenericError>("Malformed __debug_info section in " +
+ file.path() + ": too small");
+
+ if (!debugAbbrev)
+ return llvm::make_error<GenericError>("Missing __dwarf_abbrev section in " +
+ file.path());
+
+ if (auto tuOrErr = readCompUnit(normalizedFile, *debugInfo, *debugAbbrev,
+ *debugStrings, file.path())) {
+ // FIXME: Kill of allocator and code under 'copyRefs' when we fix YAML
+ // memory ownership.
+ std::unique_ptr<BumpPtrAllocator> allocator;
+ if (copyRefs) {
+ allocator = std::make_unique<BumpPtrAllocator>();
+ tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
+ tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
+ }
+ file.setDebugInfo(std::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
+ if (copyRefs)
+ file.debugInfo()->setAllocator(std::move(allocator));
+ } else
+ return tuOrErr.takeError();
+
+ return llvm::Error::success();
+}
+
+static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) {
+ if (is64)
+ return read64(addr, isBig);
+
+ int32_t res = read32(addr, isBig);
+ return res;
+}
+
+/// --- Augmentation String Processing ---
+
+struct CIEInfo {
+ bool _augmentationDataPresent = false;
+ bool _mayHaveEH = false;
+ uint32_t _offsetOfLSDA = ~0U;
+ uint32_t _offsetOfPersonality = ~0U;
+ uint32_t _offsetOfFDEPointerEncoding = ~0U;
+ uint32_t _augmentationDataLength = ~0U;
+};
+
+typedef llvm::DenseMap<const MachODefinedAtom*, CIEInfo> CIEInfoMap;
+
+static llvm::Error processAugmentationString(const uint8_t *augStr,
+ CIEInfo &cieInfo,
+ unsigned &len) {
+
+ if (augStr[0] == '\0') {
+ len = 1;
+ return llvm::Error::success();
+ }
+
+ if (augStr[0] != 'z')
+ return llvm::make_error<GenericError>("expected 'z' at start of "
+ "augmentation string");
+
+ cieInfo._augmentationDataPresent = true;
+ uint64_t idx = 1;
+
+ uint32_t offsetInAugmentationData = 0;
+ while (augStr[idx] != '\0') {
+ if (augStr[idx] == 'L') {
+ cieInfo._offsetOfLSDA = offsetInAugmentationData;
+ // This adds a single byte to the augmentation data.
+ ++offsetInAugmentationData;
+ ++idx;
+ continue;
+ }
+ if (augStr[idx] == 'P') {
+ cieInfo._offsetOfPersonality = offsetInAugmentationData;
+ // This adds a single byte to the augmentation data for the encoding,
+ // then a number of bytes for the pointer data.
+ // FIXME: We are assuming 4 is correct here for the pointer size as we
+ // always currently use delta32ToGOT.
+ offsetInAugmentationData += 5;
+ ++idx;
+ continue;
+ }
+ if (augStr[idx] == 'R') {
+ cieInfo._offsetOfFDEPointerEncoding = offsetInAugmentationData;
+ // This adds a single byte to the augmentation data.
+ ++offsetInAugmentationData;
+ ++idx;
+ continue;
+ }
+ if (augStr[idx] == 'e') {
+ if (augStr[idx + 1] != 'h')
+ return llvm::make_error<GenericError>("expected 'eh' in "
+ "augmentation string");
+ cieInfo._mayHaveEH = true;
+ idx += 2;
+ continue;
+ }
+ ++idx;
+ }
+
+ cieInfo._augmentationDataLength = offsetInAugmentationData;
+
+ len = idx + 1;
+ return llvm::Error::success();
+}
+
+static llvm::Error processCIE(const NormalizedFile &normalizedFile,
+ MachOFile &file,
+ mach_o::ArchHandler &handler,
+ const Section *ehFrameSection,
+ MachODefinedAtom *atom,
+ uint64_t offset,
+ CIEInfoMap &cieInfos) {
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ const uint8_t *frameData = atom->rawContent().data();
+
+ CIEInfo cieInfo;
+
+ uint32_t size = read32(frameData, isBig);
+ uint64_t cieIDField = size == 0xffffffffU
+ ? sizeof(uint32_t) + sizeof(uint64_t)
+ : sizeof(uint32_t);
+ uint64_t versionField = cieIDField + sizeof(uint32_t);
+ uint64_t augmentationStringField = versionField + sizeof(uint8_t);
+
+ unsigned augmentationStringLength = 0;
+ if (auto err = processAugmentationString(frameData + augmentationStringField,
+ cieInfo, augmentationStringLength))
+ return err;
+
+ if (cieInfo._offsetOfPersonality != ~0U) {
+ // If we have augmentation data for the personality function, then we may
+ // need to implicitly generate its relocation.
+
+ // Parse the EH Data field which is pointer sized.
+ uint64_t EHDataField = augmentationStringField + augmentationStringLength;
+ const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
+ unsigned EHDataFieldSize = (cieInfo._mayHaveEH ? (is64 ? 8 : 4) : 0);
+
+ // Parse Code Align Factor which is a ULEB128.
+ uint64_t CodeAlignField = EHDataField + EHDataFieldSize;
+ unsigned lengthFieldSize = 0;
+ llvm::decodeULEB128(frameData + CodeAlignField, &lengthFieldSize);
+
+ // Parse Data Align Factor which is a SLEB128.
+ uint64_t DataAlignField = CodeAlignField + lengthFieldSize;
+ llvm::decodeSLEB128(frameData + DataAlignField, &lengthFieldSize);
+
+ // Parse Return Address Register which is a byte.
+ uint64_t ReturnAddressField = DataAlignField + lengthFieldSize;
+
+ // Parse the augmentation length which is a ULEB128.
+ uint64_t AugmentationLengthField = ReturnAddressField + 1;
+ uint64_t AugmentationLength =
+ llvm::decodeULEB128(frameData + AugmentationLengthField,
+ &lengthFieldSize);
+
+ if (AugmentationLength != cieInfo._augmentationDataLength)
+ return llvm::make_error<GenericError>("CIE augmentation data length "
+ "mismatch");
+
+ // Get the start address of the augmentation data.
+ uint64_t AugmentationDataField = AugmentationLengthField + lengthFieldSize;
+
+ // Parse the personality function from the augmentation data.
+ uint64_t PersonalityField =
+ AugmentationDataField + cieInfo._offsetOfPersonality;
+
+ // Parse the personality encoding.
+ // FIXME: Verify that this is a 32-bit pcrel offset.
+ uint64_t PersonalityFunctionField = PersonalityField + 1;
+
+ if (atom->begin() != atom->end()) {
+ // If we have an explicit relocation, then make sure it matches this
+ // offset as this is where we'd expect it to be applied to.
+ DefinedAtom::reference_iterator CurrentRef = atom->begin();
+ if (CurrentRef->offsetInAtom() != PersonalityFunctionField)
+ return llvm::make_error<GenericError>("CIE personality reloc at "
+ "wrong offset");
+
+ if (++CurrentRef != atom->end())
+ return llvm::make_error<GenericError>("CIE contains too many relocs");
+ } else {
+ // Implicitly generate the personality function reloc. It's assumed to
+ // be a delta32 offset to a GOT entry.
+ // FIXME: Parse the encoding and check this.
+ int32_t funcDelta = read32(frameData + PersonalityFunctionField, isBig);
+ uint64_t funcAddress = ehFrameSection->address + offset +
+ PersonalityFunctionField;
+ funcAddress += funcDelta;
+
+ const MachODefinedAtom *func = nullptr;
+ Reference::Addend addend;
+ func = findAtomCoveringAddress(normalizedFile, file, funcAddress,
+ addend);
+ atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
+ handler.unwindRefToPersonalityFunctionKind(),
+ PersonalityFunctionField, func, addend);
+ }
+ } else if (atom->begin() != atom->end()) {
+ // Otherwise, we expect there to be no relocations in this atom as the only
+ // relocation would have been to the personality function.
+ return llvm::make_error<GenericError>("unexpected relocation in CIE");
+ }
+
+
+ cieInfos[atom] = std::move(cieInfo);
+
+ return llvm::Error::success();
+}
+
+static llvm::Error processFDE(const NormalizedFile &normalizedFile,
+ MachOFile &file,
+ mach_o::ArchHandler &handler,
+ const Section *ehFrameSection,
+ MachODefinedAtom *atom,
+ uint64_t offset,
+ const CIEInfoMap &cieInfos) {
+
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
+
+ // Compiler wasn't lazy and actually told us what it meant.
+ // Unfortunately, the compiler may not have generated references for all of
+ // [cie, func, lsda] and so we still need to parse the FDE and add references
+ // for any the compiler didn't generate.
+ if (atom->begin() != atom->end())
+ atom->sortReferences();
+
+ DefinedAtom::reference_iterator CurrentRef = atom->begin();
+
+ // This helper returns the reference (if one exists) at the offset we are
+ // currently processing. It automatically increments the ref iterator if we
+ // do return a ref, and throws an error if we pass over a ref without
+ // comsuming it.
+ auto currentRefGetter = [&CurrentRef,
+ &atom](uint64_t Offset)->const Reference* {
+ // If there are no more refs found, then we are done.
+ if (CurrentRef == atom->end())
+ return nullptr;
+
+ const Reference *Ref = *CurrentRef;
+
+ // If we haven't reached the offset for this reference, then return that
+ // we don't yet have a reference to process.
+ if (Offset < Ref->offsetInAtom())
+ return nullptr;
+
+ // If the offset is equal, then we want to process this ref.
+ if (Offset == Ref->offsetInAtom()) {
+ ++CurrentRef;
+ return Ref;
+ }
+
+ // The current ref is at an offset which is earlier than the current
+ // offset, then we failed to consume it when we should have. In this case
+ // throw an error.
+ llvm::report_fatal_error("Skipped reference when processing FDE");
+ };
+
+ // Helper to either get the reference at this current location, and verify
+ // that it is of the expected type, or add a reference of that type.
+ // Returns the reference target.
+ auto verifyOrAddReference = [&](uint64_t targetAddress,
+ Reference::KindValue refKind,
+ uint64_t refAddress,
+ bool allowsAddend)->const Atom* {
+ if (auto *ref = currentRefGetter(refAddress)) {
+ // The compiler already emitted a relocation for the CIE ref. This should
+ // have been converted to the correct type of reference in
+ // get[Pair]ReferenceInfo().
+ assert(ref->kindValue() == refKind &&
+ "Incorrect EHFrame reference kind");
+ return ref->target();
+ }
+ Reference::Addend addend;
+ auto *target = findAtomCoveringAddress(normalizedFile, file,
+ targetAddress, addend);
+ atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
+ refKind, refAddress, target, addend);
+
+ if (!allowsAddend)
+ assert(!addend && "EHFrame reference cannot have addend");
+ return target;
+ };
+
+ const uint8_t *startFrameData = atom->rawContent().data();
+ const uint8_t *frameData = startFrameData;
+
+ uint32_t size = read32(frameData, isBig);
+ uint64_t cieFieldInFDE = size == 0xffffffffU
+ ? sizeof(uint32_t) + sizeof(uint64_t)
+ : sizeof(uint32_t);
+
+ // Linker needs to fixup a reference from the FDE to its parent CIE (a
+ // 32-bit byte offset backwards in the __eh_frame section).
+ uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig);
+ uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE;
+ cieAddress -= cieDelta;
+
+ auto *cieRefTarget = verifyOrAddReference(cieAddress,
+ handler.unwindRefToCIEKind(),
+ cieFieldInFDE, false);
+ const MachODefinedAtom *cie = dyn_cast<MachODefinedAtom>(cieRefTarget);
+ assert(cie && cie->contentType() == DefinedAtom::typeCFI &&
+ "FDE's CIE field does not point at the start of a CIE.");
+
+ const CIEInfo &cieInfo = cieInfos.find(cie)->second;
+
+ // Linker needs to fixup reference from the FDE to the function it's
+ // describing. FIXME: there are actually different ways to do this, and the
+ // particular method used is specified in the CIE's augmentation fields
+ // (hopefully)
+ uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t);
+
+ int64_t functionFromFDE = readSPtr(is64, isBig,
+ frameData + rangeFieldInFDE);
+ uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE;
+ rangeStart += functionFromFDE;
+
+ verifyOrAddReference(rangeStart,
+ handler.unwindRefToFunctionKind(),
+ rangeFieldInFDE, true);
+
+ // Handle the augmentation data if there is any.
+ if (cieInfo._augmentationDataPresent) {
+ // First process the augmentation data length field.
+ uint64_t augmentationDataLengthFieldInFDE =
+ rangeFieldInFDE + 2 * (is64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ unsigned lengthFieldSize = 0;
+ uint64_t augmentationDataLength =
+ llvm::decodeULEB128(frameData + augmentationDataLengthFieldInFDE,
+ &lengthFieldSize);
+
+ if (cieInfo._offsetOfLSDA != ~0U && augmentationDataLength > 0) {
+
+ // Look at the augmentation data field.
+ uint64_t augmentationDataFieldInFDE =
+ augmentationDataLengthFieldInFDE + lengthFieldSize;
+
+ int64_t lsdaFromFDE = readSPtr(is64, isBig,
+ frameData + augmentationDataFieldInFDE);
+ uint64_t lsdaStart =
+ ehFrameSection->address + offset + augmentationDataFieldInFDE +
+ lsdaFromFDE;
+
+ verifyOrAddReference(lsdaStart,
+ handler.unwindRefToFunctionKind(),
+ augmentationDataFieldInFDE, true);
+ }
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile,
+ MachOFile &file,
+ mach_o::ArchHandler &handler) {
+
+ const Section *ehFrameSection = nullptr;
+ for (auto §ion : normalizedFile.sections)
+ if (section.segmentName == "__TEXT" &&
+ section.sectionName == "__eh_frame") {
+ ehFrameSection = §ion;
+ break;
+ }
+
+ // No __eh_frame so nothing to do.
+ if (!ehFrameSection)
+ return llvm::Error::success();
+
+ llvm::Error ehFrameErr = llvm::Error::success();
+ CIEInfoMap cieInfos;
+
+ file.eachAtomInSection(*ehFrameSection,
+ [&](MachODefinedAtom *atom, uint64_t offset) -> void {
+ assert(atom->contentType() == DefinedAtom::typeCFI);
+
+ // Bail out if we've encountered an error.
+ if (ehFrameErr)
+ return;
+
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ if (ArchHandler::isDwarfCIE(isBig, atom))
+ ehFrameErr = processCIE(normalizedFile, file, handler, ehFrameSection,
+ atom, offset, cieInfos);
+ else
+ ehFrameErr = processFDE(normalizedFile, file, handler, ehFrameSection,
+ atom, offset, cieInfos);
+ });
+
+ return ehFrameErr;
+}
+
+llvm::Error parseObjCImageInfo(const Section §,
+ const NormalizedFile &normalizedFile,
+ MachOFile &file) {
+
+ // struct objc_image_info {
+ // uint32_t version; // initially 0
+ // uint32_t flags;
+ // };
+
+ ArrayRef<uint8_t> content = sect.content;
+ if (content.size() != 8)
+ return llvm::make_error<GenericError>(sect.segmentName + "/" +
+ sect.sectionName +
+ " in file " + file.path() +
+ " should be 8 bytes in size");
+
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ uint32_t version = read32(content.data(), isBig);
+ if (version)
+ return llvm::make_error<GenericError>(sect.segmentName + "/" +
+ sect.sectionName +
+ " in file " + file.path() +
+ " should have version=0");
+
+ uint32_t flags = read32(content.data() + 4, isBig);
+ if (flags & (MachOLinkingContext::objc_supports_gc |
+ MachOLinkingContext::objc_gc_only))
+ return llvm::make_error<GenericError>(sect.segmentName + "/" +
+ sect.sectionName +
+ " in file " + file.path() +
+ " uses GC. This is not supported");
+
+ if (flags & MachOLinkingContext::objc_retainReleaseForSimulator)
+ file.setObjcConstraint(MachOLinkingContext::objc_retainReleaseForSimulator);
+ else
+ file.setObjcConstraint(MachOLinkingContext::objc_retainRelease);
+
+ file.setSwiftVersion((flags >> 8) & 0xFF);
+
+ return llvm::Error::success();
+}
+
+/// Converts normalized mach-o file into an lld::File and lld::Atoms.
+llvm::Expected<std::unique_ptr<lld::File>>
+objectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
+ bool copyRefs) {
+ auto file = std::make_unique<MachOFile>(path);
+ if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs))
+ return std::move(ec);
+ return std::unique_ptr<File>(std::move(file));
+}
+
+llvm::Expected<std::unique_ptr<lld::File>>
+dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path,
+ bool copyRefs) {
+ // Instantiate SharedLibraryFile object.
+ auto file = std::make_unique<MachODylibFile>(path);
+ if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs))
+ return std::move(ec);
+ return std::unique_ptr<File>(std::move(file));
+}
+
+} // anonymous namespace
+
+namespace normalized {
+
+static bool isObjCImageInfo(const Section §) {
+ return (sect.segmentName == "__OBJC" && sect.sectionName == "__image_info") ||
+ (sect.segmentName == "__DATA" && sect.sectionName == "__objc_imageinfo");
+}
+
+llvm::Error
+normalizedObjectToAtoms(MachOFile *file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs) {
+ LLVM_DEBUG(llvm::dbgs() << "******** Normalizing file to atoms: "
+ << file->path() << "\n");
+ bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0);
+
+ // Create atoms from each section.
+ for (auto § : normalizedFile.sections) {
+
+ // If this is a debug-info section parse it specially.
+ if (isDebugInfoSection(sect))
+ continue;
+
+ // If the file contains an objc_image_info struct, then we should parse the
+ // ObjC flags and Swift version.
+ if (isObjCImageInfo(sect)) {
+ if (auto ec = parseObjCImageInfo(sect, normalizedFile, *file))
+ return ec;
+ // We then skip adding atoms for this section as we use the ObjCPass to
+ // re-emit this data after it has been aggregated for all files.
+ continue;
+ }
+
+ bool customSectionName;
+ DefinedAtom::ContentType atomType = atomTypeFromSection(sect,
+ customSectionName);
+ if (auto ec = processSection(atomType, sect, customSectionName,
+ normalizedFile, *file, scatterable, copyRefs))
+ return ec;
+ }
+ // Create atoms from undefined symbols.
+ for (auto &sym : normalizedFile.undefinedSymbols) {
+ // Undefined symbols with n_value != 0 are actually tentative definitions.
+ if (sym.value == Hex64(0)) {
+ file->addUndefinedAtom(sym.name, copyRefs);
+ } else {
+ file->addTentativeDefAtom(sym.name, atomScope(sym.scope), sym.value,
+ DefinedAtom::Alignment(1 << (sym.desc >> 8)),
+ copyRefs);
+ }
+ }
+
+ // Convert mach-o relocations to References
+ std::unique_ptr<mach_o::ArchHandler> handler
+ = ArchHandler::create(normalizedFile.arch);
+ for (auto § : normalizedFile.sections) {
+ if (isDebugInfoSection(sect))
+ continue;
+ if (llvm::Error ec = convertRelocs(sect, normalizedFile, scatterable,
+ *file, *handler))
+ return ec;
+ }
+
+ // Add additional arch-specific References
+ file->eachDefinedAtom([&](MachODefinedAtom* atom) -> void {
+ handler->addAdditionalReferences(*atom);
+ });
+
+ // Each __eh_frame section needs references to both __text (the function we're
+ // providing unwind info for) and itself (FDE -> CIE). These aren't
+ // represented in the relocations on some architectures, so we have to add
+ // them back in manually there.
+ if (auto ec = addEHFrameReferences(normalizedFile, *file, *handler))
+ return ec;
+
+ // Process mach-o data-in-code regions array. That information is encoded in
+ // atoms as References at each transition point.
+ unsigned nextIndex = 0;
+ for (const DataInCode &entry : normalizedFile.dataInCode) {
+ ++nextIndex;
+ const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset);
+ if (!s) {
+ return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE address ("
+ + Twine(entry.offset)
+ + ") is not in any section"));
+ }
+ uint64_t offsetInSect = entry.offset - s->address;
+ uint32_t offsetInAtom;
+ MachODefinedAtom *atom = file->findAtomCoveringAddress(*s, offsetInSect,
+ &offsetInAtom);
+ if (offsetInAtom + entry.length > atom->size()) {
+ return llvm::make_error<GenericError>(Twine("LC_DATA_IN_CODE entry "
+ "(offset="
+ + Twine(entry.offset)
+ + ", length="
+ + Twine(entry.length)
+ + ") crosses atom boundary."));
+ }
+ // Add reference that marks start of data-in-code.
+ atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(),
+ handler->dataInCodeTransitionStart(*atom),
+ offsetInAtom, atom, entry.kind);
+
+ // Peek at next entry, if it starts where this one ends, skip ending ref.
+ if (nextIndex < normalizedFile.dataInCode.size()) {
+ const DataInCode &nextEntry = normalizedFile.dataInCode[nextIndex];
+ if (nextEntry.offset == (entry.offset + entry.length))
+ continue;
+ }
+
+ // If data goes to end of function, skip ending ref.
+ if ((offsetInAtom + entry.length) == atom->size())
+ continue;
+
+ // Add reference that marks end of data-in-code.
+ atom->addReference(Reference::KindNamespace::mach_o, handler->kindArch(),
+ handler->dataInCodeTransitionEnd(*atom),
+ offsetInAtom+entry.length, atom, 0);
+ }
+
+ // Cache some attributes on the file for use later.
+ file->setFlags(normalizedFile.flags);
+ file->setArch(normalizedFile.arch);
+ file->setOS(normalizedFile.os);
+ file->setMinVersion(normalizedFile.minOSverson);
+ file->setMinVersionLoadCommandKind(normalizedFile.minOSVersionKind);
+
+ // Sort references in each atom to their canonical order.
+ for (const DefinedAtom* defAtom : file->defined()) {
+ reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences();
+ }
+
+ if (auto err = parseDebugInfo(*file, normalizedFile, copyRefs))
+ return err;
+
+ return llvm::Error::success();
+}
+
+llvm::Error
+normalizedDylibToAtoms(MachODylibFile *file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs) {
+ file->setInstallName(normalizedFile.installName);
+ file->setCompatVersion(normalizedFile.compatVersion);
+ file->setCurrentVersion(normalizedFile.currentVersion);
+
+ // Tell MachODylibFile object about all symbols it exports.
+ if (!normalizedFile.exportInfo.empty()) {
+ // If exports trie exists, use it instead of traditional symbol table.
+ for (const Export &exp : normalizedFile.exportInfo) {
+ bool weakDef = (exp.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ // StringRefs from export iterator are ephemeral, so force copy.
+ file->addExportedSymbol(exp.name, weakDef, true);
+ }
+ } else {
+ for (auto &sym : normalizedFile.globalSymbols) {
+ assert((sym.scope & N_EXT) && "only expect external symbols here");
+ bool weakDef = (sym.desc & N_WEAK_DEF);
+ file->addExportedSymbol(sym.name, weakDef, copyRefs);
+ }
+ }
+ // Tell MachODylibFile object about all dylibs it re-exports.
+ for (const DependentDylib &dep : normalizedFile.dependentDylibs) {
+ if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB)
+ file->addReExportedDylib(dep.path);
+ }
+ return llvm::Error::success();
+}
+
+void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
+ StringRef &segmentName,
+ StringRef §ionName,
+ SectionType §ionType,
+ SectionAttr §ionAttrs,
+ bool &relocsToDefinedCanBeImplicit) {
+
+ for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ;
+ p->atomType != DefinedAtom::typeUnknown; ++p) {
+ if (p->atomType != atomType)
+ continue;
+ // Wild carded entries are ignored for reverse lookups.
+ if (p->segmentName.empty() || p->sectionName.empty())
+ continue;
+ segmentName = p->segmentName;
+ sectionName = p->sectionName;
+ sectionType = p->sectionType;
+ sectionAttrs = 0;
+ relocsToDefinedCanBeImplicit = false;
+ if (atomType == DefinedAtom::typeCode)
+ sectionAttrs = S_ATTR_PURE_INSTRUCTIONS;
+ if (atomType == DefinedAtom::typeCFI)
+ relocsToDefinedCanBeImplicit = true;
+ return;
+ }
+ llvm_unreachable("content type not yet supported");
+}
+
+llvm::Expected<std::unique_ptr<lld::File>>
+normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
+ bool copyRefs) {
+ switch (normalizedFile.fileType) {
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ return dylibToAtoms(normalizedFile, path, copyRefs);
+ case MH_OBJECT:
+ return objectToAtoms(normalizedFile, path, copyRefs);
+ default:
+ llvm_unreachable("unhandled MachO file type!");
+ }
+}
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+///
+/// \file For mach-o object files, this implementation uses YAML I/O to
+/// provide the convert between YAML and the normalized mach-o (NM).
+///
+/// +------------+ +------+
+/// | normalized | <-> | yaml |
+/// +------------+ +------+
+
+#include "MachONormalizedFile.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/Error.h"
+#include "lld/ReaderWriter/YamlContext.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <system_error>
+
+using llvm::StringRef;
+using namespace llvm::yaml;
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+using lld::YamlContext;
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(Segment)
+LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib)
+LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Export)
+LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode)
+
+
+// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
+namespace llvm {
+namespace yaml {
+
+// A vector of Sections is a sequence.
+template<>
+struct SequenceTraits< std::vector<Section> > {
+ static size_t size(IO &io, std::vector<Section> &seq) {
+ return seq.size();
+ }
+ static Section& element(IO &io, std::vector<Section> &seq, size_t index) {
+ if ( index >= seq.size() )
+ seq.resize(index+1);
+ return seq[index];
+ }
+};
+
+template<>
+struct SequenceTraits< std::vector<Symbol> > {
+ static size_t size(IO &io, std::vector<Symbol> &seq) {
+ return seq.size();
+ }
+ static Symbol& element(IO &io, std::vector<Symbol> &seq, size_t index) {
+ if ( index >= seq.size() )
+ seq.resize(index+1);
+ return seq[index];
+ }
+};
+
+// A vector of Relocations is a sequence.
+template<>
+struct SequenceTraits< Relocations > {
+ static size_t size(IO &io, Relocations &seq) {
+ return seq.size();
+ }
+ static Relocation& element(IO &io, Relocations &seq, size_t index) {
+ if ( index >= seq.size() )
+ seq.resize(index+1);
+ return seq[index];
+ }
+};
+
+// The content for a section is represented as a flow sequence of hex bytes.
+template<>
+struct SequenceTraits< ContentBytes > {
+ static size_t size(IO &io, ContentBytes &seq) {
+ return seq.size();
+ }
+ static Hex8& element(IO &io, ContentBytes &seq, size_t index) {
+ if ( index >= seq.size() )
+ seq.resize(index+1);
+ return seq[index];
+ }
+ static const bool flow = true;
+};
+
+// The indirect symbols for a section is represented as a flow sequence
+// of numbers (symbol table indexes).
+template<>
+struct SequenceTraits< IndirectSymbols > {
+ static size_t size(IO &io, IndirectSymbols &seq) {
+ return seq.size();
+ }
+ static uint32_t& element(IO &io, IndirectSymbols &seq, size_t index) {
+ if ( index >= seq.size() )
+ seq.resize(index+1);
+ return seq[index];
+ }
+ static const bool flow = true;
+};
+
+template <>
+struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> {
+ static void enumeration(IO &io, lld::MachOLinkingContext::Arch &value) {
+ io.enumCase(value, "unknown",lld::MachOLinkingContext::arch_unknown);
+ io.enumCase(value, "ppc", lld::MachOLinkingContext::arch_ppc);
+ io.enumCase(value, "x86", lld::MachOLinkingContext::arch_x86);
+ io.enumCase(value, "x86_64", lld::MachOLinkingContext::arch_x86_64);
+ io.enumCase(value, "armv6", lld::MachOLinkingContext::arch_armv6);
+ io.enumCase(value, "armv7", lld::MachOLinkingContext::arch_armv7);
+ io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s);
+ io.enumCase(value, "arm64", lld::MachOLinkingContext::arch_arm64);
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<lld::MachOLinkingContext::OS> {
+ static void enumeration(IO &io, lld::MachOLinkingContext::OS &value) {
+ io.enumCase(value, "unknown",
+ lld::MachOLinkingContext::OS::unknown);
+ io.enumCase(value, "Mac OS X",
+ lld::MachOLinkingContext::OS::macOSX);
+ io.enumCase(value, "iOS",
+ lld::MachOLinkingContext::OS::iOS);
+ io.enumCase(value, "iOS Simulator",
+ lld::MachOLinkingContext::OS::iOS_simulator);
+ }
+};
+
+
+template <>
+struct ScalarEnumerationTraits<HeaderFileType> {
+ static void enumeration(IO &io, HeaderFileType &value) {
+ io.enumCase(value, "MH_OBJECT", llvm::MachO::MH_OBJECT);
+ io.enumCase(value, "MH_DYLIB", llvm::MachO::MH_DYLIB);
+ io.enumCase(value, "MH_EXECUTE", llvm::MachO::MH_EXECUTE);
+ io.enumCase(value, "MH_BUNDLE", llvm::MachO::MH_BUNDLE);
+ }
+};
+
+
+template <>
+struct ScalarBitSetTraits<FileFlags> {
+ static void bitset(IO &io, FileFlags &value) {
+ io.bitSetCase(value, "MH_TWOLEVEL",
+ llvm::MachO::MH_TWOLEVEL);
+ io.bitSetCase(value, "MH_SUBSECTIONS_VIA_SYMBOLS",
+ llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ }
+};
+
+
+template <>
+struct ScalarEnumerationTraits<SectionType> {
+ static void enumeration(IO &io, SectionType &value) {
+ io.enumCase(value, "S_REGULAR",
+ llvm::MachO::S_REGULAR);
+ io.enumCase(value, "S_ZEROFILL",
+ llvm::MachO::S_ZEROFILL);
+ io.enumCase(value, "S_CSTRING_LITERALS",
+ llvm::MachO::S_CSTRING_LITERALS);
+ io.enumCase(value, "S_4BYTE_LITERALS",
+ llvm::MachO::S_4BYTE_LITERALS);
+ io.enumCase(value, "S_8BYTE_LITERALS",
+ llvm::MachO::S_8BYTE_LITERALS);
+ io.enumCase(value, "S_LITERAL_POINTERS",
+ llvm::MachO::S_LITERAL_POINTERS);
+ io.enumCase(value, "S_NON_LAZY_SYMBOL_POINTERS",
+ llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS);
+ io.enumCase(value, "S_LAZY_SYMBOL_POINTERS",
+ llvm::MachO::S_LAZY_SYMBOL_POINTERS);
+ io.enumCase(value, "S_SYMBOL_STUBS",
+ llvm::MachO::S_SYMBOL_STUBS);
+ io.enumCase(value, "S_MOD_INIT_FUNC_POINTERS",
+ llvm::MachO::S_MOD_INIT_FUNC_POINTERS);
+ io.enumCase(value, "S_MOD_TERM_FUNC_POINTERS",
+ llvm::MachO::S_MOD_TERM_FUNC_POINTERS);
+ io.enumCase(value, "S_COALESCED",
+ llvm::MachO::S_COALESCED);
+ io.enumCase(value, "S_GB_ZEROFILL",
+ llvm::MachO::S_GB_ZEROFILL);
+ io.enumCase(value, "S_INTERPOSING",
+ llvm::MachO::S_INTERPOSING);
+ io.enumCase(value, "S_16BYTE_LITERALS",
+ llvm::MachO::S_16BYTE_LITERALS);
+ io.enumCase(value, "S_DTRACE_DOF",
+ llvm::MachO::S_DTRACE_DOF);
+ io.enumCase(value, "S_LAZY_DYLIB_SYMBOL_POINTERS",
+ llvm::MachO::S_LAZY_DYLIB_SYMBOL_POINTERS);
+ io.enumCase(value, "S_THREAD_LOCAL_REGULAR",
+ llvm::MachO::S_THREAD_LOCAL_REGULAR);
+ io.enumCase(value, "S_THREAD_LOCAL_ZEROFILL",
+ llvm::MachO::S_THREAD_LOCAL_ZEROFILL);
+ io.enumCase(value, "S_THREAD_LOCAL_VARIABLES",
+ llvm::MachO::S_THREAD_LOCAL_VARIABLES);
+ io.enumCase(value, "S_THREAD_LOCAL_VARIABLE_POINTERS",
+ llvm::MachO::S_THREAD_LOCAL_VARIABLE_POINTERS);
+ io.enumCase(value, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS",
+ llvm::MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS);
+ }
+};
+
+template <>
+struct ScalarBitSetTraits<SectionAttr> {
+ static void bitset(IO &io, SectionAttr &value) {
+ io.bitSetCase(value, "S_ATTR_PURE_INSTRUCTIONS",
+ llvm::MachO::S_ATTR_PURE_INSTRUCTIONS);
+ io.bitSetCase(value, "S_ATTR_SOME_INSTRUCTIONS",
+ llvm::MachO::S_ATTR_SOME_INSTRUCTIONS);
+ io.bitSetCase(value, "S_ATTR_NO_DEAD_STRIP",
+ llvm::MachO::S_ATTR_NO_DEAD_STRIP);
+ io.bitSetCase(value, "S_ATTR_EXT_RELOC",
+ llvm::MachO::S_ATTR_EXT_RELOC);
+ io.bitSetCase(value, "S_ATTR_LOC_RELOC",
+ llvm::MachO::S_ATTR_LOC_RELOC);
+ io.bitSetCase(value, "S_ATTR_DEBUG",
+ llvm::MachO::S_ATTR_DEBUG);
+ }
+};
+
+/// This is a custom formatter for SectionAlignment. Values are
+/// the power to raise by, ie, the n in 2^n.
+template <> struct ScalarTraits<SectionAlignment> {
+ static void output(const SectionAlignment &value, void *ctxt,
+ raw_ostream &out) {
+ out << llvm::format("%d", (uint32_t)value);
+ }
+
+ static StringRef input(StringRef scalar, void *ctxt,
+ SectionAlignment &value) {
+ uint32_t alignment;
+ if (scalar.getAsInteger(0, alignment)) {
+ return "malformed alignment value";
+ }
+ if (!llvm::isPowerOf2_32(alignment))
+ return "alignment must be a power of 2";
+ value = alignment;
+ return StringRef(); // returning empty string means success
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+template <>
+struct ScalarEnumerationTraits<NListType> {
+ static void enumeration(IO &io, NListType &value) {
+ io.enumCase(value, "N_UNDF", llvm::MachO::N_UNDF);
+ io.enumCase(value, "N_ABS", llvm::MachO::N_ABS);
+ io.enumCase(value, "N_SECT", llvm::MachO::N_SECT);
+ io.enumCase(value, "N_PBUD", llvm::MachO::N_PBUD);
+ io.enumCase(value, "N_INDR", llvm::MachO::N_INDR);
+ }
+};
+
+template <>
+struct ScalarBitSetTraits<SymbolScope> {
+ static void bitset(IO &io, SymbolScope &value) {
+ io.bitSetCase(value, "N_EXT", llvm::MachO::N_EXT);
+ io.bitSetCase(value, "N_PEXT", llvm::MachO::N_PEXT);
+ }
+};
+
+template <>
+struct ScalarBitSetTraits<SymbolDesc> {
+ static void bitset(IO &io, SymbolDesc &value) {
+ io.bitSetCase(value, "N_NO_DEAD_STRIP", llvm::MachO::N_NO_DEAD_STRIP);
+ io.bitSetCase(value, "N_WEAK_REF", llvm::MachO::N_WEAK_REF);
+ io.bitSetCase(value, "N_WEAK_DEF", llvm::MachO::N_WEAK_DEF);
+ io.bitSetCase(value, "N_ARM_THUMB_DEF", llvm::MachO::N_ARM_THUMB_DEF);
+ io.bitSetCase(value, "N_SYMBOL_RESOLVER", llvm::MachO::N_SYMBOL_RESOLVER);
+ }
+};
+
+
+template <>
+struct MappingTraits<Section> {
+ struct NormalizedContentBytes;
+ static void mapping(IO &io, Section §) {
+ io.mapRequired("segment", sect.segmentName);
+ io.mapRequired("section", sect.sectionName);
+ io.mapRequired("type", sect.type);
+ io.mapOptional("attributes", sect.attributes);
+ io.mapOptional("alignment", sect.alignment, (SectionAlignment)1);
+ io.mapRequired("address", sect.address);
+ if (isZeroFillSection(sect.type)) {
+ // S_ZEROFILL sections use "size:" instead of "content:"
+ uint64_t size = sect.content.size();
+ io.mapOptional("size", size);
+ if (!io.outputting()) {
+ uint8_t *bytes = nullptr;
+ sect.content = makeArrayRef(bytes, size);
+ }
+ } else {
+ MappingNormalization<NormalizedContent, ArrayRef<uint8_t>> content(
+ io, sect.content);
+ io.mapOptional("content", content->_normalizedContent);
+ }
+ io.mapOptional("relocations", sect.relocations);
+ io.mapOptional("indirect-syms", sect.indirectSymbols);
+ }
+
+ struct NormalizedContent {
+ NormalizedContent(IO &io) : _io(io) {}
+ NormalizedContent(IO &io, ArrayRef<uint8_t> content) : _io(io) {
+ // When writing yaml, copy content byte array to Hex8 vector.
+ for (auto &c : content) {
+ _normalizedContent.push_back(c);
+ }
+ }
+ ArrayRef<uint8_t> denormalize(IO &io) {
+ // When reading yaml, allocate byte array owned by NormalizedFile and
+ // copy Hex8 vector to byte array.
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ NormalizedFile *file = info->_normalizeMachOFile;
+ assert(file != nullptr);
+ size_t size = _normalizedContent.size();
+ if (!size)
+ return None;
+ uint8_t *bytes = file->ownedAllocations.Allocate<uint8_t>(size);
+ std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes);
+ return makeArrayRef(bytes, size);
+ }
+
+ IO &_io;
+ ContentBytes _normalizedContent;
+ };
+};
+
+
+template <>
+struct MappingTraits<Relocation> {
+ static void mapping(IO &io, Relocation &reloc) {
+ io.mapRequired("offset", reloc.offset);
+ io.mapOptional("scattered", reloc.scattered, false);
+ io.mapRequired("type", reloc.type);
+ io.mapRequired("length", reloc.length);
+ io.mapRequired("pc-rel", reloc.pcRel);
+ if ( !reloc.scattered )
+ io.mapRequired("extern", reloc.isExtern);
+ if ( reloc.scattered )
+ io.mapRequired("value", reloc.value);
+ if ( !reloc.scattered )
+ io.mapRequired("symbol", reloc.symbol);
+ }
+};
+
+
+template <>
+struct ScalarEnumerationTraits<RelocationInfoType> {
+ static void enumeration(IO &io, RelocationInfoType &value) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ NormalizedFile *file = info->_normalizeMachOFile;
+ assert(file != nullptr);
+ switch (file->arch) {
+ case lld::MachOLinkingContext::arch_x86_64:
+ io.enumCase(value, "X86_64_RELOC_UNSIGNED",
+ llvm::MachO::X86_64_RELOC_UNSIGNED);
+ io.enumCase(value, "X86_64_RELOC_SIGNED",
+ llvm::MachO::X86_64_RELOC_SIGNED);
+ io.enumCase(value, "X86_64_RELOC_BRANCH",
+ llvm::MachO::X86_64_RELOC_BRANCH);
+ io.enumCase(value, "X86_64_RELOC_GOT_LOAD",
+ llvm::MachO::X86_64_RELOC_GOT_LOAD);
+ io.enumCase(value, "X86_64_RELOC_GOT",
+ llvm::MachO::X86_64_RELOC_GOT);
+ io.enumCase(value, "X86_64_RELOC_SUBTRACTOR",
+ llvm::MachO::X86_64_RELOC_SUBTRACTOR);
+ io.enumCase(value, "X86_64_RELOC_SIGNED_1",
+ llvm::MachO::X86_64_RELOC_SIGNED_1);
+ io.enumCase(value, "X86_64_RELOC_SIGNED_2",
+ llvm::MachO::X86_64_RELOC_SIGNED_2);
+ io.enumCase(value, "X86_64_RELOC_SIGNED_4",
+ llvm::MachO::X86_64_RELOC_SIGNED_4);
+ io.enumCase(value, "X86_64_RELOC_TLV",
+ llvm::MachO::X86_64_RELOC_TLV);
+ break;
+ case lld::MachOLinkingContext::arch_x86:
+ io.enumCase(value, "GENERIC_RELOC_VANILLA",
+ llvm::MachO::GENERIC_RELOC_VANILLA);
+ io.enumCase(value, "GENERIC_RELOC_PAIR",
+ llvm::MachO::GENERIC_RELOC_PAIR);
+ io.enumCase(value, "GENERIC_RELOC_SECTDIFF",
+ llvm::MachO::GENERIC_RELOC_SECTDIFF);
+ io.enumCase(value, "GENERIC_RELOC_LOCAL_SECTDIFF",
+ llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
+ io.enumCase(value, "GENERIC_RELOC_TLV",
+ llvm::MachO::GENERIC_RELOC_TLV);
+ break;
+ case lld::MachOLinkingContext::arch_armv6:
+ case lld::MachOLinkingContext::arch_armv7:
+ case lld::MachOLinkingContext::arch_armv7s:
+ io.enumCase(value, "ARM_RELOC_VANILLA",
+ llvm::MachO::ARM_RELOC_VANILLA);
+ io.enumCase(value, "ARM_RELOC_PAIR",
+ llvm::MachO::ARM_RELOC_PAIR);
+ io.enumCase(value, "ARM_RELOC_SECTDIFF",
+ llvm::MachO::ARM_RELOC_SECTDIFF);
+ io.enumCase(value, "ARM_RELOC_LOCAL_SECTDIFF",
+ llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF);
+ io.enumCase(value, "ARM_RELOC_BR24",
+ llvm::MachO::ARM_RELOC_BR24);
+ io.enumCase(value, "ARM_THUMB_RELOC_BR22",
+ llvm::MachO::ARM_THUMB_RELOC_BR22);
+ io.enumCase(value, "ARM_RELOC_HALF",
+ llvm::MachO::ARM_RELOC_HALF);
+ io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF",
+ llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
+ break;
+ case lld::MachOLinkingContext::arch_arm64:
+ io.enumCase(value, "ARM64_RELOC_UNSIGNED",
+ llvm::MachO::ARM64_RELOC_UNSIGNED);
+ io.enumCase(value, "ARM64_RELOC_SUBTRACTOR",
+ llvm::MachO::ARM64_RELOC_SUBTRACTOR);
+ io.enumCase(value, "ARM64_RELOC_BRANCH26",
+ llvm::MachO::ARM64_RELOC_BRANCH26);
+ io.enumCase(value, "ARM64_RELOC_PAGE21",
+ llvm::MachO::ARM64_RELOC_PAGE21);
+ io.enumCase(value, "ARM64_RELOC_PAGEOFF12",
+ llvm::MachO::ARM64_RELOC_PAGEOFF12);
+ io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21",
+ llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21);
+ io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
+ llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12);
+ io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT",
+ llvm::MachO::ARM64_RELOC_POINTER_TO_GOT);
+ io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21",
+ llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21);
+ io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
+ llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
+ io.enumCase(value, "ARM64_RELOC_ADDEND",
+ llvm::MachO::ARM64_RELOC_ADDEND);
+ break;
+ default:
+ llvm_unreachable("unknown architecture");
+ }
+ }
+};
+
+
+template <>
+struct MappingTraits<Symbol> {
+ static void mapping(IO &io, Symbol& sym) {
+ io.mapRequired("name", sym.name);
+ io.mapRequired("type", sym.type);
+ io.mapOptional("scope", sym.scope, SymbolScope(0));
+ io.mapOptional("sect", sym.sect, (uint8_t)0);
+ if (sym.type == llvm::MachO::N_UNDF) {
+ // In undef symbols, desc field contains alignment/ordinal info
+ // which is better represented as a hex vaule.
+ uint16_t t1 = sym.desc;
+ Hex16 t2 = t1;
+ io.mapOptional("desc", t2, Hex16(0));
+ sym.desc = t2;
+ } else {
+ // In defined symbols, desc fit is a set of option bits.
+ io.mapOptional("desc", sym.desc, SymbolDesc(0));
+ }
+ io.mapRequired("value", sym.value);
+ }
+};
+
+// Custom mapping for VMProtect (e.g. "r-x").
+template <>
+struct ScalarTraits<VMProtect> {
+ static void output(const VMProtect &value, void*, raw_ostream &out) {
+ out << ( (value & llvm::MachO::VM_PROT_READ) ? 'r' : '-');
+ out << ( (value & llvm::MachO::VM_PROT_WRITE) ? 'w' : '-');
+ out << ( (value & llvm::MachO::VM_PROT_EXECUTE) ? 'x' : '-');
+ }
+ static StringRef input(StringRef scalar, void*, VMProtect &value) {
+ value = 0;
+ if (scalar.size() != 3)
+ return "segment access protection must be three chars (e.g. \"r-x\")";
+ switch (scalar[0]) {
+ case 'r':
+ value = llvm::MachO::VM_PROT_READ;
+ break;
+ case '-':
+ break;
+ default:
+ return "segment access protection first char must be 'r' or '-'";
+ }
+ switch (scalar[1]) {
+ case 'w':
+ value = value | llvm::MachO::VM_PROT_WRITE;
+ break;
+ case '-':
+ break;
+ default:
+ return "segment access protection second char must be 'w' or '-'";
+ }
+ switch (scalar[2]) {
+ case 'x':
+ value = value | llvm::MachO::VM_PROT_EXECUTE;
+ break;
+ case '-':
+ break;
+ default:
+ return "segment access protection third char must be 'x' or '-'";
+ }
+ // Return the empty string on success,
+ return StringRef();
+ }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+
+template <>
+struct MappingTraits<Segment> {
+ static void mapping(IO &io, Segment& seg) {
+ io.mapRequired("name", seg.name);
+ io.mapRequired("address", seg.address);
+ io.mapRequired("size", seg.size);
+ io.mapRequired("init-access", seg.init_access);
+ io.mapRequired("max-access", seg.max_access);
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<LoadCommandType> {
+ static void enumeration(IO &io, LoadCommandType &value) {
+ io.enumCase(value, "LC_LOAD_DYLIB",
+ llvm::MachO::LC_LOAD_DYLIB);
+ io.enumCase(value, "LC_LOAD_WEAK_DYLIB",
+ llvm::MachO::LC_LOAD_WEAK_DYLIB);
+ io.enumCase(value, "LC_REEXPORT_DYLIB",
+ llvm::MachO::LC_REEXPORT_DYLIB);
+ io.enumCase(value, "LC_LOAD_UPWARD_DYLIB",
+ llvm::MachO::LC_LOAD_UPWARD_DYLIB);
+ io.enumCase(value, "LC_LAZY_LOAD_DYLIB",
+ llvm::MachO::LC_LAZY_LOAD_DYLIB);
+ io.enumCase(value, "LC_VERSION_MIN_MACOSX",
+ llvm::MachO::LC_VERSION_MIN_MACOSX);
+ io.enumCase(value, "LC_VERSION_MIN_IPHONEOS",
+ llvm::MachO::LC_VERSION_MIN_IPHONEOS);
+ io.enumCase(value, "LC_VERSION_MIN_TVOS",
+ llvm::MachO::LC_VERSION_MIN_TVOS);
+ io.enumCase(value, "LC_VERSION_MIN_WATCHOS",
+ llvm::MachO::LC_VERSION_MIN_WATCHOS);
+ }
+};
+
+template <>
+struct MappingTraits<DependentDylib> {
+ static void mapping(IO &io, DependentDylib& dylib) {
+ io.mapRequired("path", dylib.path);
+ io.mapOptional("kind", dylib.kind,
+ llvm::MachO::LC_LOAD_DYLIB);
+ io.mapOptional("compat-version", dylib.compatVersion,
+ PackedVersion(0x10000));
+ io.mapOptional("current-version", dylib.currentVersion,
+ PackedVersion(0x10000));
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<RebaseType> {
+ static void enumeration(IO &io, RebaseType &value) {
+ io.enumCase(value, "REBASE_TYPE_POINTER",
+ llvm::MachO::REBASE_TYPE_POINTER);
+ io.enumCase(value, "REBASE_TYPE_TEXT_PCREL32",
+ llvm::MachO::REBASE_TYPE_TEXT_PCREL32);
+ io.enumCase(value, "REBASE_TYPE_TEXT_ABSOLUTE32",
+ llvm::MachO::REBASE_TYPE_TEXT_ABSOLUTE32);
+ }
+};
+
+
+template <>
+struct MappingTraits<RebaseLocation> {
+ static void mapping(IO &io, RebaseLocation& rebase) {
+ io.mapRequired("segment-index", rebase.segIndex);
+ io.mapRequired("segment-offset", rebase.segOffset);
+ io.mapOptional("kind", rebase.kind,
+ llvm::MachO::REBASE_TYPE_POINTER);
+ }
+};
+
+
+
+template <>
+struct ScalarEnumerationTraits<BindType> {
+ static void enumeration(IO &io, BindType &value) {
+ io.enumCase(value, "BIND_TYPE_POINTER",
+ llvm::MachO::BIND_TYPE_POINTER);
+ io.enumCase(value, "BIND_TYPE_TEXT_ABSOLUTE32",
+ llvm::MachO::BIND_TYPE_TEXT_ABSOLUTE32);
+ io.enumCase(value, "BIND_TYPE_TEXT_PCREL32",
+ llvm::MachO::BIND_TYPE_TEXT_PCREL32);
+ }
+};
+
+template <>
+struct MappingTraits<BindLocation> {
+ static void mapping(IO &io, BindLocation &bind) {
+ io.mapRequired("segment-index", bind.segIndex);
+ io.mapRequired("segment-offset", bind.segOffset);
+ io.mapOptional("kind", bind.kind,
+ llvm::MachO::BIND_TYPE_POINTER);
+ io.mapOptional("can-be-null", bind.canBeNull, false);
+ io.mapRequired("ordinal", bind.ordinal);
+ io.mapRequired("symbol-name", bind.symbolName);
+ io.mapOptional("addend", bind.addend, Hex64(0));
+ }
+};
+
+
+template <>
+struct ScalarEnumerationTraits<ExportSymbolKind> {
+ static void enumeration(IO &io, ExportSymbolKind &value) {
+ io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_REGULAR",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
+ io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
+ io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
+ }
+};
+
+template <>
+struct ScalarBitSetTraits<ExportFlags> {
+ static void bitset(IO &io, ExportFlags &value) {
+ io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_REEXPORT",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
+ io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER",
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
+ }
+};
+
+
+template <>
+struct MappingTraits<Export> {
+ static void mapping(IO &io, Export &exp) {
+ io.mapRequired("name", exp.name);
+ io.mapOptional("offset", exp.offset);
+ io.mapOptional("kind", exp.kind,
+ llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR);
+ if (!io.outputting() || exp.flags)
+ io.mapOptional("flags", exp.flags);
+ io.mapOptional("other", exp.otherOffset, Hex32(0));
+ io.mapOptional("other-name", exp.otherName, StringRef());
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<DataRegionType> {
+ static void enumeration(IO &io, DataRegionType &value) {
+ io.enumCase(value, "DICE_KIND_DATA",
+ llvm::MachO::DICE_KIND_DATA);
+ io.enumCase(value, "DICE_KIND_JUMP_TABLE8",
+ llvm::MachO::DICE_KIND_JUMP_TABLE8);
+ io.enumCase(value, "DICE_KIND_JUMP_TABLE16",
+ llvm::MachO::DICE_KIND_JUMP_TABLE16);
+ io.enumCase(value, "DICE_KIND_JUMP_TABLE32",
+ llvm::MachO::DICE_KIND_JUMP_TABLE32);
+ io.enumCase(value, "DICE_KIND_ABS_JUMP_TABLE32",
+ llvm::MachO::DICE_KIND_ABS_JUMP_TABLE32);
+ }
+};
+
+template <>
+struct MappingTraits<DataInCode> {
+ static void mapping(IO &io, DataInCode &entry) {
+ io.mapRequired("offset", entry.offset);
+ io.mapRequired("length", entry.length);
+ io.mapRequired("kind", entry.kind);
+ }
+};
+
+template <>
+struct ScalarTraits<PackedVersion> {
+ static void output(const PackedVersion &value, void*, raw_ostream &out) {
+ out << llvm::format("%d.%d", (value >> 16), (value >> 8) & 0xFF);
+ if (value & 0xFF) {
+ out << llvm::format(".%d", (value & 0xFF));
+ }
+ }
+ static StringRef input(StringRef scalar, void*, PackedVersion &result) {
+ uint32_t value;
+ if (lld::MachOLinkingContext::parsePackedVersion(scalar, value))
+ return "malformed version number";
+ result = value;
+ // Return the empty string on success,
+ return StringRef();
+ }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+template <>
+struct MappingTraits<NormalizedFile> {
+ static void mapping(IO &io, NormalizedFile &file) {
+ io.mapRequired("arch", file.arch);
+ io.mapRequired("file-type", file.fileType);
+ io.mapOptional("flags", file.flags);
+ io.mapOptional("dependents", file.dependentDylibs);
+ io.mapOptional("install-name", file.installName, StringRef());
+ io.mapOptional("compat-version", file.compatVersion, PackedVersion(0x10000));
+ io.mapOptional("current-version", file.currentVersion, PackedVersion(0x10000));
+ io.mapOptional("has-UUID", file.hasUUID, true);
+ io.mapOptional("rpaths", file.rpaths);
+ io.mapOptional("entry-point", file.entryAddress, Hex64(0));
+ io.mapOptional("stack-size", file.stackSize, Hex64(0));
+ io.mapOptional("source-version", file.sourceVersion, Hex64(0));
+ io.mapOptional("OS", file.os);
+ io.mapOptional("min-os-version", file.minOSverson, PackedVersion(0));
+ io.mapOptional("min-os-version-kind", file.minOSVersionKind, (LoadCommandType)0);
+ io.mapOptional("sdk-version", file.sdkVersion, PackedVersion(0));
+ io.mapOptional("segments", file.segments);
+ io.mapOptional("sections", file.sections);
+ io.mapOptional("local-symbols", file.localSymbols);
+ io.mapOptional("global-symbols", file.globalSymbols);
+ io.mapOptional("undefined-symbols",file.undefinedSymbols);
+ io.mapOptional("page-size", file.pageSize, Hex32(4096));
+ io.mapOptional("rebasings", file.rebasingInfo);
+ io.mapOptional("bindings", file.bindingInfo);
+ io.mapOptional("weak-bindings", file.weakBindingInfo);
+ io.mapOptional("lazy-bindings", file.lazyBindingInfo);
+ io.mapOptional("exports", file.exportInfo);
+ io.mapOptional("dataInCode", file.dataInCode);
+ }
+ static std::string validate(IO &io, NormalizedFile &file) { return {}; }
+};
+
+} // namespace llvm
+} // namespace yaml
+
+
+namespace lld {
+namespace mach_o {
+
+/// Handles !mach-o tagged yaml documents.
+bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io,
+ const lld::File *&file) const {
+ if (!io.mapTag("!mach-o"))
+ return false;
+ // Step 1: parse yaml into normalized mach-o struct.
+ NormalizedFile nf;
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ assert(info->_normalizeMachOFile == nullptr);
+ info->_normalizeMachOFile = &nf;
+ MappingTraits<NormalizedFile>::mapping(io, nf);
+ // Step 2: parse normalized mach-o struct into atoms.
+ auto fileOrError = normalizedToAtoms(nf, info->_path, true);
+
+ // Check that we parsed successfully.
+ if (!fileOrError) {
+ std::string buffer;
+ llvm::raw_string_ostream stream(buffer);
+ handleAllErrors(fileOrError.takeError(),
+ [&](const llvm::ErrorInfoBase &EI) {
+ EI.log(stream);
+ stream << "\n";
+ });
+ io.setError(stream.str());
+ return false;
+ }
+
+ if (nf.arch != _arch) {
+ io.setError(Twine("file is wrong architecture. Expected ("
+ + MachOLinkingContext::nameFromArch(_arch)
+ + ") found ("
+ + MachOLinkingContext::nameFromArch(nf.arch)
+ + ")"));
+ return false;
+ }
+ info->_normalizeMachOFile = nullptr;
+ file = fileOrError->release();
+ return true;
+}
+
+
+
+namespace normalized {
+
+/// Parses a yaml encoded mach-o file to produce an in-memory normalized view.
+llvm::Expected<std::unique_ptr<NormalizedFile>>
+readYaml(std::unique_ptr<MemoryBuffer> &mb) {
+ // Make empty NormalizedFile.
+ std::unique_ptr<NormalizedFile> f(new NormalizedFile());
+
+ // Create YAML Input parser.
+ YamlContext yamlContext;
+ yamlContext._normalizeMachOFile = f.get();
+ llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
+
+ // Fill NormalizedFile by parsing yaml.
+ yin >> *f;
+
+ // Return error if there were parsing problems.
+ if (auto ec = yin.error())
+ return llvm::make_error<GenericError>(Twine("YAML parsing error: ")
+ + ec.message());
+
+ // Hand ownership of instantiated NormalizedFile to caller.
+ return std::move(f);
+}
+
+
+/// Writes a yaml encoded mach-o files from an in-memory normalized view.
+std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) {
+ // YAML I/O is not const aware, so need to cast away ;-(
+ NormalizedFile *f = const_cast<NormalizedFile*>(&file);
+
+ // Create yaml Output writer, using yaml options for context.
+ YamlContext yamlContext;
+ yamlContext._normalizeMachOFile = f;
+ llvm::yaml::Output yout(out, &yamlContext);
+
+ // Stream out yaml.
+ yout << *f;
+
+ return std::error_code();
+}
+
+} // namespace normalized
+} // namespace mach_o
+} // namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/MachOPasses.h -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_PASSES_H
+#define LLD_READER_WRITER_MACHO_PASSES_H
+
+#include "lld/Core/PassManager.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+
+namespace lld {
+namespace mach_o {
+
+void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx);
+void addShimPass(PassManager &pm, const MachOLinkingContext &ctx);
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_PASSES_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/ObjCPass.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachONormalizedFileBinaryUtils.h"
+#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace lld {
+namespace mach_o {
+
+///
+/// ObjC Image Info Atom created by the ObjC pass.
+///
+class ObjCImageInfoAtom : public SimpleDefinedAtom {
+public:
+ ObjCImageInfoAtom(const File &file, bool isBig,
+ MachOLinkingContext::ObjCConstraint objCConstraint,
+ uint32_t swiftVersion)
+ : SimpleDefinedAtom(file) {
+
+ Data.info.version = 0;
+
+ switch (objCConstraint) {
+ case MachOLinkingContext::objc_unknown:
+ llvm_unreachable("Shouldn't run the objc pass without a constraint");
+ case MachOLinkingContext::objc_supports_gc:
+ case MachOLinkingContext::objc_gc_only:
+ llvm_unreachable("GC is not supported");
+ case MachOLinkingContext::objc_retainReleaseForSimulator:
+ // The retain/release for simulator flag is already the correct
+ // encoded value for the data so just set it here.
+ Data.info.flags = (uint32_t)objCConstraint;
+ break;
+ case MachOLinkingContext::objc_retainRelease:
+ // We don't need to encode this flag, so just leave the flags as 0.
+ Data.info.flags = 0;
+ break;
+ }
+
+ Data.info.flags |= (swiftVersion << 8);
+
+ normalized::write32(Data.bytes + 4, Data.info.flags, isBig);
+ }
+
+ ~ObjCImageInfoAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeObjCImageInfo;
+ }
+
+ Alignment alignment() const override {
+ return 4;
+ }
+
+ uint64_t size() const override {
+ return 8;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR__;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(Data.bytes, size());
+ }
+
+private:
+
+ struct objc_image_info {
+ uint32_t version;
+ uint32_t flags;
+ };
+
+ union {
+ objc_image_info info;
+ uint8_t bytes[8];
+ } Data;
+};
+
+class ObjCPass : public Pass {
+public:
+ ObjCPass(const MachOLinkingContext &context)
+ : _ctx(context),
+ _file(*_ctx.make_file<MachOFile>("<mach-o objc pass>")) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ // Add the image info.
+ mergedFile.addAtom(*getImageInfo());
+
+ return llvm::Error::success();
+ }
+
+private:
+
+ const DefinedAtom* getImageInfo() {
+ bool IsBig = MachOLinkingContext::isBigEndian(_ctx.arch());
+ return new (_file.allocator()) ObjCImageInfoAtom(_file, IsBig,
+ _ctx.objcConstraint(),
+ _ctx.swiftVersion());
+ }
+
+ const MachOLinkingContext &_ctx;
+ MachOFile &_file;
+};
+
+
+
+void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ pm.add(std::make_unique<ObjCPass>(ctx));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===---- lib/ReaderWriter/MachO/SectCreateFile.h ---------------*- c++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
+#define LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
+
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+
+namespace lld {
+namespace mach_o {
+
+//
+// A FlateNamespaceFile instance may be added as a resolution source of last
+// resort, depending on how -flat_namespace and -undefined are set.
+//
+class SectCreateFile : public File {
+public:
+ class SectCreateAtom : public SimpleDefinedAtom {
+ public:
+ SectCreateAtom(const File &file, StringRef segName, StringRef sectName,
+ std::unique_ptr<MemoryBuffer> content)
+ : SimpleDefinedAtom(file),
+ _combinedName((segName + "/" + sectName).str()),
+ _content(std::move(content)) {}
+
+ ~SectCreateAtom() override = default;
+
+ uint64_t size() const override { return _content->getBufferSize(); }
+
+ Scope scope() const override { return scopeGlobal; }
+
+ ContentType contentType() const override { return typeSectCreate; }
+
+ SectionChoice sectionChoice() const override { return sectionCustomRequired; }
+
+ StringRef customSectionName() const override { return _combinedName; }
+
+ DeadStripKind deadStrip() const override { return deadStripNever; }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ const uint8_t *data =
+ reinterpret_cast<const uint8_t*>(_content->getBufferStart());
+ return ArrayRef<uint8_t>(data, _content->getBufferSize());
+ }
+
+ StringRef segmentName() const { return _segName; }
+ StringRef sectionName() const { return _sectName; }
+
+ private:
+ std::string _combinedName;
+ StringRef _segName;
+ StringRef _sectName;
+ std::unique_ptr<MemoryBuffer> _content;
+ };
+
+ SectCreateFile() : File("sectcreate", kindSectCreateObject) {}
+
+ void addSection(StringRef seg, StringRef sect,
+ std::unique_ptr<MemoryBuffer> content) {
+ _definedAtoms.push_back(
+ new (allocator()) SectCreateAtom(*this, seg, sect, std::move(content)));
+ }
+
+ const AtomRange<DefinedAtom> defined() const override {
+ return _definedAtoms;
+ }
+
+ const AtomRange<UndefinedAtom> undefined() const override {
+ return _noUndefinedAtoms;
+ }
+
+ const AtomRange<SharedLibraryAtom> sharedLibrary() const override {
+ return _noSharedLibraryAtoms;
+ }
+
+ const AtomRange<AbsoluteAtom> absolute() const override {
+ return _noAbsoluteAtoms;
+ }
+
+ void clearAtoms() override {
+ _definedAtoms.clear();
+ _noUndefinedAtoms.clear();
+ _noSharedLibraryAtoms.clear();
+ _noAbsoluteAtoms.clear();
+ }
+
+private:
+ AtomVector<DefinedAtom> _definedAtoms;
+};
+
+} // namespace mach_o
+} // namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_SECTCREATE_FILE_H
--- /dev/null
+//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This linker pass updates branch-sites whose target is a different mode
+// (thumb vs arm).
+//
+// Arm code has two instruction encodings thumb and arm. When branching from
+// one code encoding to another, you need to use an instruction that switches
+// the instruction mode. Usually the transition only happens at call sites, and
+// the linker can transform a BL instruction in BLX (or vice versa). But if the
+// compiler did a tail call optimization and a function ends with a branch (not
+// branch and link), there is no pc-rel BX instruction.
+//
+// The ShimPass looks for pc-rel B instructions that will need to switch mode.
+// For those cases it synthesizes a shim which does the transition, then
+// modifies the original atom with the B instruction to target to the shim atom.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace lld {
+namespace mach_o {
+
+class ShimPass : public Pass {
+public:
+ ShimPass(const MachOLinkingContext &context)
+ : _ctx(context), _archHandler(_ctx.archHandler()),
+ _stubInfo(_archHandler.stubInfo()),
+ _file(*_ctx.make_file<MachOFile>("<mach-o shim pass>")) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ // Scan all references in all atoms.
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ for (const Reference *ref : *atom) {
+ // Look at non-call branches.
+ if (!_archHandler.isNonCallBranch(*ref))
+ continue;
+ const Atom *target = ref->target();
+ assert(target != nullptr);
+ if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) {
+ bool atomIsThumb = _archHandler.isThumbFunction(*atom);
+ bool targetIsThumb = _archHandler.isThumbFunction(*daTarget);
+ if (atomIsThumb != targetIsThumb)
+ updateBranchToUseShim(atomIsThumb, *daTarget, ref);
+ }
+ }
+ }
+ // Exit early if no shims needed.
+ if (_targetToShim.empty())
+ return llvm::Error::success();
+
+ // Sort shim atoms so the layout order is stable.
+ std::vector<const DefinedAtom *> shims;
+ shims.reserve(_targetToShim.size());
+ for (auto element : _targetToShim) {
+ shims.push_back(element.second);
+ }
+ std::sort(shims.begin(), shims.end(),
+ [](const DefinedAtom *l, const DefinedAtom *r) {
+ return (l->name() < r->name());
+ });
+
+ // Add all shims to master file.
+ for (const DefinedAtom *shim : shims)
+ mergedFile.addAtom(*shim);
+
+ return llvm::Error::success();
+ }
+
+private:
+
+ void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target,
+ const Reference *ref) {
+ // Make file-format specific stub and other support atoms.
+ const DefinedAtom *shim = this->getShim(thumbToArm, target);
+ assert(shim != nullptr);
+ // Switch branch site to target shim atom.
+ const_cast<Reference *>(ref)->setTarget(shim);
+ }
+
+ const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) {
+ auto pos = _targetToShim.find(&target);
+ if ( pos != _targetToShim.end() ) {
+ // Reuse an existing shim.
+ assert(pos->second != nullptr);
+ return pos->second;
+ } else {
+ // There is no existing shim, so create a new one.
+ const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm,
+ target);
+ _targetToShim[&target] = shim;
+ return shim;
+ }
+ }
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ const ArchHandler::StubInfo &_stubInfo;
+ MachOFile &_file;
+ llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim;
+};
+
+
+
+void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ pm.add(std::make_unique<ShimPass>(ctx));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/StubsPass.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This linker pass updates call-sites which have references to shared library
+// atoms to instead have a reference to a stub (PLT entry) for the specified
+// symbol. Each file format defines a subclass of StubsPass which implements
+// the abstract methods for creating the file format specific StubAtoms.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/Simple.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace lld {
+namespace mach_o {
+
+//
+// Lazy Pointer Atom created by the stubs pass.
+//
+class LazyPointerAtom : public SimpleDefinedAtom {
+public:
+ LazyPointerAtom(const File &file, bool is64)
+ : SimpleDefinedAtom(file), _is64(is64) { }
+
+ ~LazyPointerAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeLazyPointer;
+ }
+
+ Alignment alignment() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ uint64_t size() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permRW_;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t zeros[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ return llvm::makeArrayRef(zeros, size());
+ }
+
+private:
+ const bool _is64;
+};
+
+//
+// NonLazyPointer (GOT) Atom created by the stubs pass.
+//
+class NonLazyPointerAtom : public SimpleDefinedAtom {
+public:
+ NonLazyPointerAtom(const File &file, bool is64, ContentType contentType)
+ : SimpleDefinedAtom(file), _is64(is64), _contentType(contentType) { }
+
+ ~NonLazyPointerAtom() override = default;
+
+ ContentType contentType() const override {
+ return _contentType;
+ }
+
+ Alignment alignment() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ uint64_t size() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permRW_;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t zeros[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ return llvm::makeArrayRef(zeros, size());
+ }
+
+private:
+ const bool _is64;
+ const ContentType _contentType;
+};
+
+//
+// Stub Atom created by the stubs pass.
+//
+class StubAtom : public SimpleDefinedAtom {
+public:
+ StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
+ : SimpleDefinedAtom(file), _stubInfo(stubInfo){ }
+
+ ~StubAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeStub;
+ }
+
+ Alignment alignment() const override {
+ return 1 << _stubInfo.codeAlignment;
+ }
+
+ uint64_t size() const override {
+ return _stubInfo.stubSize;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR_X;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize);
+ }
+
+private:
+ const ArchHandler::StubInfo &_stubInfo;
+};
+
+//
+// Stub Helper Atom created by the stubs pass.
+//
+class StubHelperAtom : public SimpleDefinedAtom {
+public:
+ StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
+ : SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
+
+ ~StubHelperAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeStubHelper;
+ }
+
+ Alignment alignment() const override {
+ return 1 << _stubInfo.codeAlignment;
+ }
+
+ uint64_t size() const override {
+ return _stubInfo.stubHelperSize;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR_X;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(_stubInfo.stubHelperBytes,
+ _stubInfo.stubHelperSize);
+ }
+
+private:
+ const ArchHandler::StubInfo &_stubInfo;
+};
+
+//
+// Stub Helper Common Atom created by the stubs pass.
+//
+class StubHelperCommonAtom : public SimpleDefinedAtom {
+public:
+ StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo)
+ : SimpleDefinedAtom(file), _stubInfo(stubInfo) { }
+
+ ~StubHelperCommonAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeStubHelper;
+ }
+
+ Alignment alignment() const override {
+ return 1 << _stubInfo.stubHelperCommonAlignment;
+ }
+
+ uint64_t size() const override {
+ return _stubInfo.stubHelperCommonSize;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permR_X;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes,
+ _stubInfo.stubHelperCommonSize);
+ }
+
+private:
+ const ArchHandler::StubInfo &_stubInfo;
+};
+
+class StubsPass : public Pass {
+public:
+ StubsPass(const MachOLinkingContext &context)
+ : _ctx(context), _archHandler(_ctx.archHandler()),
+ _stubInfo(_archHandler.stubInfo()),
+ _file(*_ctx.make_file<MachOFile>("<mach-o Stubs pass>")) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ // Skip this pass if output format uses text relocations instead of stubs.
+ if (!this->noTextRelocs())
+ return llvm::Error::success();
+
+ // Scan all references in all atoms.
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ for (const Reference *ref : *atom) {
+ // Look at call-sites.
+ if (!this->isCallSite(*ref))
+ continue;
+ const Atom *target = ref->target();
+ assert(target != nullptr);
+ if (isa<SharedLibraryAtom>(target)) {
+ // Calls to shared libraries go through stubs.
+ _targetToUses[target].push_back(ref);
+ continue;
+ }
+ const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target);
+ if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){
+ // Calls to interposable functions in same linkage unit must also go
+ // through a stub.
+ assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit);
+ _targetToUses[target].push_back(ref);
+ }
+ }
+ }
+
+ // Exit early if no stubs needed.
+ if (_targetToUses.empty())
+ return llvm::Error::success();
+
+ // First add help-common and GOT slots used by lazy binding.
+ SimpleDefinedAtom *helperCommonAtom =
+ new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo);
+ SimpleDefinedAtom *helperCacheNLPAtom =
+ new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
+ _stubInfo.stubHelperImageCacheContentType);
+ SimpleDefinedAtom *helperBinderNLPAtom =
+ new (_file.allocator()) NonLazyPointerAtom(_file, _ctx.is64Bit(),
+ _stubInfo.stubHelperImageCacheContentType);
+ addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
+ helperCacheNLPAtom);
+ addOptReference(
+ helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache,
+ _stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom);
+ addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
+ helperBinderNLPAtom);
+ addOptReference(
+ helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder,
+ _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom);
+ mergedFile.addAtom(*helperCommonAtom);
+ mergedFile.addAtom(*helperBinderNLPAtom);
+ mergedFile.addAtom(*helperCacheNLPAtom);
+
+ // Add reference to dyld_stub_binder in libSystem.dylib
+ auto I = llvm::find_if(
+ mergedFile.sharedLibrary(), [&](const SharedLibraryAtom *atom) {
+ return atom->name().equals(_stubInfo.binderSymbolName);
+ });
+ assert(I != mergedFile.sharedLibrary().end() &&
+ "dyld_stub_binder not found");
+ addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I);
+
+ // Sort targets by name, so stubs and lazy pointers are consistent
+ std::vector<const Atom *> targetsNeedingStubs;
+ for (auto it : _targetToUses)
+ targetsNeedingStubs.push_back(it.first);
+ std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(),
+ [](const Atom * left, const Atom * right) {
+ return (left->name().compare(right->name()) < 0);
+ });
+
+ // Make and append stubs, lazy pointers, and helpers in alphabetical order.
+ unsigned lazyOffset = 0;
+ for (const Atom *target : targetsNeedingStubs) {
+ auto *stub = new (_file.allocator()) StubAtom(_file, _stubInfo);
+ auto *lp =
+ new (_file.allocator()) LazyPointerAtom(_file, _ctx.is64Bit());
+ auto *helper = new (_file.allocator()) StubHelperAtom(_file, _stubInfo);
+
+ addReference(stub, _stubInfo.stubReferenceToLP, lp);
+ addOptReference(stub, _stubInfo.stubReferenceToLP,
+ _stubInfo.optStubReferenceToLP, lp);
+ addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper);
+ addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target);
+ addReference(helper, _stubInfo.stubHelperReferenceToImm, helper);
+ addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper,
+ lazyOffset);
+ addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon,
+ helperCommonAtom);
+
+ mergedFile.addAtom(*stub);
+ mergedFile.addAtom(*lp);
+ mergedFile.addAtom(*helper);
+
+ // Update each reference to use stub.
+ for (const Reference *ref : _targetToUses[target]) {
+ assert(ref->target() == target);
+ // Switch call site to reference stub atom instead.
+ const_cast<Reference *>(ref)->setTarget(stub);
+ }
+
+ // Calculate new offset
+ lazyOffset += target->name().size() + 12;
+ }
+
+ return llvm::Error::success();
+ }
+
+private:
+ bool noTextRelocs() {
+ return true;
+ }
+
+ bool isCallSite(const Reference &ref) {
+ return _archHandler.isCallSite(ref);
+ }
+
+ void addReference(SimpleDefinedAtom* atom,
+ const ArchHandler::ReferenceInfo &refInfo,
+ const lld::Atom* target) {
+ atom->addReference(Reference::KindNamespace::mach_o,
+ refInfo.arch, refInfo.kind, refInfo.offset,
+ target, refInfo.addend);
+ }
+
+ void addReferenceAddend(SimpleDefinedAtom *atom,
+ const ArchHandler::ReferenceInfo &refInfo,
+ const lld::Atom *target, uint64_t addend) {
+ atom->addReference(Reference::KindNamespace::mach_o, refInfo.arch,
+ refInfo.kind, refInfo.offset, target, addend);
+ }
+
+ void addOptReference(SimpleDefinedAtom* atom,
+ const ArchHandler::ReferenceInfo &refInfo,
+ const ArchHandler::OptionalRefInfo &optRef,
+ const lld::Atom* target) {
+ if (!optRef.used)
+ return;
+ atom->addReference(Reference::KindNamespace::mach_o,
+ refInfo.arch, optRef.kind, optRef.offset,
+ target, optRef.addend);
+ }
+
+ typedef llvm::DenseMap<const Atom*,
+ llvm::SmallVector<const Reference *, 8>> TargetToUses;
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ const ArchHandler::StubInfo &_stubInfo;
+ MachOFile &_file;
+ TargetToUses _targetToUses;
+};
+
+void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ pm.add(std::unique_ptr<Pass>(new StubsPass(ctx)));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This linker pass transforms all TLV references to real references.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ArchHandler.h"
+#include "File.h"
+#include "MachOPasses.h"
+#include "lld/Core/Simple.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Debug.h"
+
+namespace lld {
+namespace mach_o {
+
+//
+// TLVP Entry Atom created by the TLV pass.
+//
+class TLVPEntryAtom : public SimpleDefinedAtom {
+public:
+ TLVPEntryAtom(const File &file, bool is64, StringRef name)
+ : SimpleDefinedAtom(file), _is64(is64), _name(name) {}
+
+ ~TLVPEntryAtom() override = default;
+
+ ContentType contentType() const override {
+ return DefinedAtom::typeTLVInitializerPtr;
+ }
+
+ Alignment alignment() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ uint64_t size() const override {
+ return _is64 ? 8 : 4;
+ }
+
+ ContentPermissions permissions() const override {
+ return DefinedAtom::permRW_;
+ }
+
+ ArrayRef<uint8_t> rawContent() const override {
+ static const uint8_t zeros[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ return llvm::makeArrayRef(zeros, size());
+ }
+
+ StringRef slotName() const {
+ return _name;
+ }
+
+private:
+ const bool _is64;
+ StringRef _name;
+};
+
+class TLVPass : public Pass {
+public:
+ TLVPass(const MachOLinkingContext &context)
+ : _ctx(context), _archHandler(_ctx.archHandler()),
+ _file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) {
+ _file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
+ }
+
+private:
+ llvm::Error perform(SimpleFile &mergedFile) override {
+ bool allowTLV = _ctx.minOS("10.7", "1.0");
+
+ for (const DefinedAtom *atom : mergedFile.defined()) {
+ for (const Reference *ref : *atom) {
+ if (!_archHandler.isTLVAccess(*ref))
+ continue;
+
+ if (!allowTLV)
+ return llvm::make_error<GenericError>(
+ "targeted OS version does not support use of thread local "
+ "variables in " + atom->name() + " for architecture " +
+ _ctx.archName());
+
+ const Atom *target = ref->target();
+ assert(target != nullptr);
+
+ const DefinedAtom *tlvpEntry = makeTLVPEntry(target);
+ const_cast<Reference*>(ref)->setTarget(tlvpEntry);
+ _archHandler.updateReferenceToTLV(ref);
+ }
+ }
+
+ std::vector<const TLVPEntryAtom*> entries;
+ entries.reserve(_targetToTLVP.size());
+ for (auto &it : _targetToTLVP)
+ entries.push_back(it.second);
+ std::sort(entries.begin(), entries.end(),
+ [](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) {
+ return (lhs->slotName().compare(rhs->slotName()) < 0);
+ });
+
+ for (const TLVPEntryAtom *slot : entries)
+ mergedFile.addAtom(*slot);
+
+ return llvm::Error::success();
+ }
+
+ const DefinedAtom *makeTLVPEntry(const Atom *target) {
+ auto pos = _targetToTLVP.find(target);
+
+ if (pos != _targetToTLVP.end())
+ return pos->second;
+
+ auto *tlvpEntry = new (_file.allocator())
+ TLVPEntryAtom(_file, _ctx.is64Bit(), target->name());
+ _targetToTLVP[target] = tlvpEntry;
+ const ArchHandler::ReferenceInfo &nlInfo =
+ _archHandler.stubInfo().nonLazyPointerReferenceToBinder;
+ tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
+ nlInfo.kind, 0, target, 0);
+ return tlvpEntry;
+ }
+
+ const MachOLinkingContext &_ctx;
+ mach_o::ArchHandler &_archHandler;
+ MachOFile &_file;
+ llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP;
+};
+
+void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
+ assert(ctx.needsTLVPass());
+ pm.add(std::make_unique<TLVPass>(ctx));
+}
+
+} // end namespace mach_o
+} // end namespace lld
--- /dev/null
+//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExecutableAtoms.h"
+#include "MachONormalizedFile.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Writer.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <system_error>
+
+using lld::mach_o::normalized::NormalizedFile;
+
+namespace lld {
+namespace mach_o {
+
+class MachOWriter : public Writer {
+public:
+ MachOWriter(const MachOLinkingContext &ctxt) : _ctx(ctxt) {}
+
+ llvm::Error writeFile(const lld::File &file, StringRef path) override {
+ // Construct empty normalized file from atoms.
+ llvm::Expected<std::unique_ptr<NormalizedFile>> nFile =
+ normalized::normalizedFromAtoms(file, _ctx);
+ if (auto ec = nFile.takeError())
+ return ec;
+
+ // For testing, write out yaml form of normalized file.
+ if (_ctx.printAtoms()) {
+ std::unique_ptr<Writer> yamlWriter = createWriterYAML(_ctx);
+ if (auto ec = yamlWriter->writeFile(file, "-"))
+ return ec;
+ }
+
+ // Write normalized file as mach-o binary.
+ return writeBinary(*nFile->get(), path);
+ }
+
+ void createImplicitFiles(std::vector<std::unique_ptr<File>> &r) override {
+ // When building main executables, add _main as required entry point.
+ if (_ctx.outputTypeHasEntry())
+ r.emplace_back(new CEntryFile(_ctx));
+ // If this can link with dylibs, need helper function (dyld_stub_binder).
+ if (_ctx.needsStubsPass())
+ r.emplace_back(new StubHelperFile(_ctx));
+ // Final linked images can access a symbol for their mach_header.
+ if (_ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
+ r.emplace_back(new MachHeaderAliasFile(_ctx));
+ }
+private:
+ const MachOLinkingContext &_ctx;
+ };
+
+
+} // namespace mach_o
+
+std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &context) {
+ return std::unique_ptr<Writer>(new lld::mach_o::MachOWriter(context));
+}
+
+} // namespace lld
--- /dev/null
+add_lld_library(lldYAML
+ ReaderWriterYAML.cpp
+
+ LINK_COMPONENTS
+ Support
+
+ LINK_LIBS
+ lldCore
+ )
--- /dev/null
+//===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/AbsoluteAtom.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/Atom.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/Error.h"
+#include "lld/Core/File.h"
+#include "lld/Core/LinkingContext.h"
+#include "lld/Core/Reader.h"
+#include "lld/Core/Reference.h"
+#include "lld/Core/SharedLibraryAtom.h"
+#include "lld/Core/Simple.h"
+#include "lld/Core/UndefinedAtom.h"
+#include "lld/Core/Writer.h"
+#include "lld/ReaderWriter/YamlContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using llvm::file_magic;
+using llvm::yaml::MappingTraits;
+using llvm::yaml::ScalarEnumerationTraits;
+using llvm::yaml::ScalarTraits;
+using llvm::yaml::IO;
+using llvm::yaml::SequenceTraits;
+using llvm::yaml::DocumentListTraits;
+
+using namespace lld;
+
+/// The conversion of Atoms to and from YAML uses LLVM's YAML I/O. This
+/// file just defines template specializations on the lld types which control
+/// how the mapping is done to and from YAML.
+
+namespace {
+
+/// Used when writing yaml files.
+/// In most cases, atoms names are unambiguous, so references can just
+/// use the atom name as the target (e.g. target: foo). But in a few
+/// cases that does not work, so ref-names are added. These are labels
+/// used only in yaml. The labels do not exist in the Atom model.
+///
+/// One need for ref-names are when atoms have no user supplied name
+/// (e.g. c-string literal). Another case is when two object files with
+/// identically named static functions are merged (ld -r) into one object file.
+/// In that case referencing the function by name is ambiguous, so a unique
+/// ref-name is added.
+class RefNameBuilder {
+public:
+ RefNameBuilder(const lld::File &file)
+ : _collisionCount(0), _unnamedCounter(0) {
+ // visit all atoms
+ for (const lld::DefinedAtom *atom : file.defined()) {
+ // Build map of atoms names to detect duplicates
+ if (!atom->name().empty())
+ buildDuplicateNameMap(*atom);
+
+ // Find references to unnamed atoms and create ref-names for them.
+ for (const lld::Reference *ref : *atom) {
+ // create refname for any unnamed reference target
+ const lld::Atom *target = ref->target();
+ if ((target != nullptr) && target->name().empty()) {
+ std::string storage;
+ llvm::raw_string_ostream buffer(storage);
+ buffer << llvm::format("L%03d", _unnamedCounter++);
+ StringRef newName = copyString(buffer.str());
+ _refNames[target] = std::string(newName);
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "unnamed atom: creating ref-name: '"
+ << newName << "' ("
+ << (const void *)newName.data() << ", "
+ << newName.size() << ")\n");
+ }
+ }
+ }
+ for (const lld::UndefinedAtom *undefAtom : file.undefined()) {
+ buildDuplicateNameMap(*undefAtom);
+ }
+ for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
+ buildDuplicateNameMap(*shlibAtom);
+ }
+ for (const lld::AbsoluteAtom *absAtom : file.absolute()) {
+ if (!absAtom->name().empty())
+ buildDuplicateNameMap(*absAtom);
+ }
+ }
+
+ void buildDuplicateNameMap(const lld::Atom &atom) {
+ assert(!atom.name().empty());
+ NameToAtom::iterator pos = _nameMap.find(atom.name());
+ if (pos != _nameMap.end()) {
+ // Found name collision, give each a unique ref-name.
+ std::string Storage;
+ llvm::raw_string_ostream buffer(Storage);
+ buffer << atom.name() << llvm::format(".%03d", ++_collisionCount);
+ StringRef newName = copyString(buffer.str());
+ _refNames[&atom] = std::string(newName);
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "name collision: creating ref-name: '"
+ << newName << "' ("
+ << (const void *)newName.data()
+ << ", " << newName.size() << ")\n");
+ const lld::Atom *prevAtom = pos->second;
+ AtomToRefName::iterator pos2 = _refNames.find(prevAtom);
+ if (pos2 == _refNames.end()) {
+ // Only create ref-name for previous if none already created.
+ std::string Storage2;
+ llvm::raw_string_ostream buffer2(Storage2);
+ buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount);
+ StringRef newName2 = copyString(buffer2.str());
+ _refNames[prevAtom] = std::string(newName2);
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "name collision: creating ref-name: '"
+ << newName2 << "' ("
+ << (const void *)newName2.data() << ", "
+ << newName2.size() << ")\n");
+ }
+ } else {
+ // First time we've seen this name, just add it to map.
+ _nameMap[atom.name()] = &atom;
+ DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
+ << "atom name seen for first time: '"
+ << atom.name() << "' ("
+ << (const void *)atom.name().data()
+ << ", " << atom.name().size() << ")\n");
+ }
+ }
+
+ bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); }
+
+ StringRef refName(const lld::Atom *atom) {
+ return _refNames.find(atom)->second;
+ }
+
+private:
+ typedef llvm::StringMap<const lld::Atom *> NameToAtom;
+ typedef llvm::DenseMap<const lld::Atom *, std::string> AtomToRefName;
+
+ // Allocate a new copy of this string in _storage, so the strings
+ // can be freed when RefNameBuilder is destroyed.
+ StringRef copyString(StringRef str) {
+ char *s = _storage.Allocate<char>(str.size());
+ memcpy(s, str.data(), str.size());
+ return StringRef(s, str.size());
+ }
+
+ unsigned int _collisionCount;
+ unsigned int _unnamedCounter;
+ NameToAtom _nameMap;
+ AtomToRefName _refNames;
+ llvm::BumpPtrAllocator _storage;
+};
+
+/// Used when reading yaml files to find the target of a reference
+/// that could be a name or ref-name.
+class RefNameResolver {
+public:
+ RefNameResolver(const lld::File *file, IO &io);
+
+ const lld::Atom *lookup(StringRef name) const {
+ NameToAtom::const_iterator pos = _nameMap.find(name);
+ if (pos != _nameMap.end())
+ return pos->second;
+ _io.setError(Twine("no such atom name: ") + name);
+ return nullptr;
+ }
+
+private:
+ typedef llvm::StringMap<const lld::Atom *> NameToAtom;
+
+ void add(StringRef name, const lld::Atom *atom) {
+ if (_nameMap.count(name)) {
+ _io.setError(Twine("duplicate atom name: ") + name);
+ } else {
+ _nameMap[name] = atom;
+ }
+ }
+
+ IO &_io;
+ NameToAtom _nameMap;
+};
+
+/// Mapping of Atoms.
+template <typename T> class AtomList {
+ using Ty = std::vector<OwningAtomPtr<T>>;
+
+public:
+ typename Ty::iterator begin() { return _atoms.begin(); }
+ typename Ty::iterator end() { return _atoms.end(); }
+ Ty _atoms;
+};
+
+/// Mapping of kind: field in yaml files.
+enum FileKinds {
+ fileKindObjectAtoms, // atom based object file encoded in yaml
+ fileKindArchive, // static archive library encoded in yaml
+ fileKindObjectMachO // mach-o object files encoded in yaml
+};
+
+struct ArchMember {
+ FileKinds _kind;
+ StringRef _name;
+ const lld::File *_content;
+};
+
+// The content bytes in a DefinedAtom are just uint8_t but we want
+// special formatting, so define a strong type.
+LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8)
+
+// SharedLibraryAtoms have a bool canBeNull() method which we'd like to be
+// more readable than just true/false.
+LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull)
+
+// lld::Reference::Kind is a tuple of <namespace, arch, value>.
+// For yaml, we just want one string that encapsulates the tuple.
+struct RefKind {
+ Reference::KindNamespace ns;
+ Reference::KindArch arch;
+ Reference::KindValue value;
+};
+
+} // end anonymous namespace
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember)
+LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *)
+// Always write DefinedAtoms content bytes as a flow sequence.
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8)
+
+// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
+namespace llvm {
+namespace yaml {
+
+// This is a custom formatter for RefKind
+template <> struct ScalarTraits<RefKind> {
+ static void output(const RefKind &kind, void *ctxt, raw_ostream &out) {
+ assert(ctxt != nullptr);
+ YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
+ assert(info->_registry);
+ StringRef str;
+ if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value,
+ str))
+ out << str;
+ else
+ out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value;
+ }
+
+ static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) {
+ assert(ctxt != nullptr);
+ YamlContext *info = reinterpret_cast<YamlContext *>(ctxt);
+ assert(info->_registry);
+ if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch,
+ kind.value))
+ return StringRef();
+ return StringRef("unknown reference kind");
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+template <> struct ScalarEnumerationTraits<lld::File::Kind> {
+ static void enumeration(IO &io, lld::File::Kind &value) {
+ io.enumCase(value, "error-object", lld::File::kindErrorObject);
+ io.enumCase(value, "object", lld::File::kindMachObject);
+ io.enumCase(value, "shared-library", lld::File::kindSharedLibrary);
+ io.enumCase(value, "static-library", lld::File::kindArchiveLibrary);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::Atom::Scope> {
+ static void enumeration(IO &io, lld::Atom::Scope &value) {
+ io.enumCase(value, "global", lld::Atom::scopeGlobal);
+ io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit);
+ io.enumCase(value, "static", lld::Atom::scopeTranslationUnit);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> {
+ static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) {
+ io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent);
+ io.enumCase(value, "custom", lld::DefinedAtom::sectionCustomPreferred);
+ io.enumCase(value, "custom-required",
+ lld::DefinedAtom::sectionCustomRequired);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> {
+ static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) {
+ io.enumCase(value, "no", DefinedAtom::interposeNo);
+ io.enumCase(value, "yes", DefinedAtom::interposeYes);
+ io.enumCase(value, "yes-and-weak", DefinedAtom::interposeYesAndRuntimeWeak);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> {
+ static void enumeration(IO &io, lld::DefinedAtom::Merge &value) {
+ io.enumCase(value, "no", lld::DefinedAtom::mergeNo);
+ io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative);
+ io.enumCase(value, "as-weak", lld::DefinedAtom::mergeAsWeak);
+ io.enumCase(value, "as-addressed-weak",
+ lld::DefinedAtom::mergeAsWeakAndAddressUsed);
+ io.enumCase(value, "by-content", lld::DefinedAtom::mergeByContent);
+ io.enumCase(value, "same-name-and-size",
+ lld::DefinedAtom::mergeSameNameAndSize);
+ io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> {
+ static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) {
+ io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal);
+ io.enumCase(value, "never", lld::DefinedAtom::deadStripNever);
+ io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DynamicExport> {
+ static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) {
+ io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal);
+ io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> {
+ static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) {
+ io.enumCase(value, "none", lld::DefinedAtom::codeNA);
+ io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC);
+ io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro);
+ io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC);
+ io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16);
+ io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb);
+ io.enumCase(value, "arm-a", lld::DefinedAtom::codeARM_a);
+ io.enumCase(value, "arm-d", lld::DefinedAtom::codeARM_d);
+ io.enumCase(value, "arm-t", lld::DefinedAtom::codeARM_t);
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> {
+ static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) {
+ io.enumCase(value, "---", lld::DefinedAtom::perm___);
+ io.enumCase(value, "r--", lld::DefinedAtom::permR__);
+ io.enumCase(value, "r-x", lld::DefinedAtom::permR_X);
+ io.enumCase(value, "rw-", lld::DefinedAtom::permRW_);
+ io.enumCase(value, "rwx", lld::DefinedAtom::permRWX);
+ io.enumCase(value, "rw-l", lld::DefinedAtom::permRW_L);
+ io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> {
+ static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) {
+ io.enumCase(value, "unknown", DefinedAtom::typeUnknown);
+ io.enumCase(value, "code", DefinedAtom::typeCode);
+ io.enumCase(value, "stub", DefinedAtom::typeStub);
+ io.enumCase(value, "constant", DefinedAtom::typeConstant);
+ io.enumCase(value, "data", DefinedAtom::typeData);
+ io.enumCase(value, "quick-data", DefinedAtom::typeDataFast);
+ io.enumCase(value, "zero-fill", DefinedAtom::typeZeroFill);
+ io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast);
+ io.enumCase(value, "const-data", DefinedAtom::typeConstData);
+ io.enumCase(value, "got", DefinedAtom::typeGOT);
+ io.enumCase(value, "resolver", DefinedAtom::typeResolver);
+ io.enumCase(value, "branch-island", DefinedAtom::typeBranchIsland);
+ io.enumCase(value, "branch-shim", DefinedAtom::typeBranchShim);
+ io.enumCase(value, "stub-helper", DefinedAtom::typeStubHelper);
+ io.enumCase(value, "c-string", DefinedAtom::typeCString);
+ io.enumCase(value, "utf16-string", DefinedAtom::typeUTF16String);
+ io.enumCase(value, "unwind-cfi", DefinedAtom::typeCFI);
+ io.enumCase(value, "unwind-lsda", DefinedAtom::typeLSDA);
+ io.enumCase(value, "const-4-byte", DefinedAtom::typeLiteral4);
+ io.enumCase(value, "const-8-byte", DefinedAtom::typeLiteral8);
+ io.enumCase(value, "const-16-byte", DefinedAtom::typeLiteral16);
+ io.enumCase(value, "lazy-pointer", DefinedAtom::typeLazyPointer);
+ io.enumCase(value, "lazy-dylib-pointer",
+ DefinedAtom::typeLazyDylibPointer);
+ io.enumCase(value, "cfstring", DefinedAtom::typeCFString);
+ io.enumCase(value, "initializer-pointer",
+ DefinedAtom::typeInitializerPtr);
+ io.enumCase(value, "terminator-pointer",
+ DefinedAtom::typeTerminatorPtr);
+ io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr);
+ io.enumCase(value, "objc-class-pointer",
+ DefinedAtom::typeObjCClassPtr);
+ io.enumCase(value, "objc-category-list",
+ DefinedAtom::typeObjC2CategoryList);
+ io.enumCase(value, "objc-image-info",
+ DefinedAtom::typeObjCImageInfo);
+ io.enumCase(value, "objc-method-list",
+ DefinedAtom::typeObjCMethodList);
+ io.enumCase(value, "objc-class1", DefinedAtom::typeObjC1Class);
+ io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF);
+ io.enumCase(value, "interposing-tuples",
+ DefinedAtom::typeInterposingTuples);
+ io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO);
+ io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo);
+ io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo);
+ io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV);
+ io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData);
+ io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill);
+ io.enumCase(value, "tlv-initializer-ptr",
+ DefinedAtom::typeTLVInitializerPtr);
+ io.enumCase(value, "mach_header", DefinedAtom::typeMachHeader);
+ io.enumCase(value, "dso_handle", DefinedAtom::typeDSOHandle);
+ io.enumCase(value, "sectcreate", DefinedAtom::typeSectCreate);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> {
+ static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) {
+ io.enumCase(value, "never", lld::UndefinedAtom::canBeNullNever);
+ io.enumCase(value, "at-runtime", lld::UndefinedAtom::canBeNullAtRuntime);
+ io.enumCase(value, "at-buildtime",lld::UndefinedAtom::canBeNullAtBuildtime);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<ShlibCanBeNull> {
+ static void enumeration(IO &io, ShlibCanBeNull &value) {
+ io.enumCase(value, "never", false);
+ io.enumCase(value, "at-runtime", true);
+ }
+};
+
+template <>
+struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> {
+ static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) {
+ io.enumCase(value, "code", lld::SharedLibraryAtom::Type::Code);
+ io.enumCase(value, "data", lld::SharedLibraryAtom::Type::Data);
+ io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown);
+ }
+};
+
+/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look
+/// like:
+/// 8 # 8-byte aligned
+/// 7 mod 16 # 16-byte aligned plus 7 bytes
+template <> struct ScalarTraits<lld::DefinedAtom::Alignment> {
+ static void output(const lld::DefinedAtom::Alignment &value, void *ctxt,
+ raw_ostream &out) {
+ if (value.modulus == 0) {
+ out << llvm::format("%d", value.value);
+ } else {
+ out << llvm::format("%d mod %d", value.modulus, value.value);
+ }
+ }
+
+ static StringRef input(StringRef scalar, void *ctxt,
+ lld::DefinedAtom::Alignment &value) {
+ value.modulus = 0;
+ size_t modStart = scalar.find("mod");
+ if (modStart != StringRef::npos) {
+ StringRef modStr = scalar.slice(0, modStart);
+ modStr = modStr.rtrim();
+ unsigned int modulus;
+ if (modStr.getAsInteger(0, modulus)) {
+ return "malformed alignment modulus";
+ }
+ value.modulus = modulus;
+ scalar = scalar.drop_front(modStart + 3);
+ scalar = scalar.ltrim();
+ }
+ unsigned int power;
+ if (scalar.getAsInteger(0, power)) {
+ return "malformed alignment power";
+ }
+ value.value = power;
+ if (value.modulus >= power) {
+ return "malformed alignment, modulus too large for power";
+ }
+ return StringRef(); // returning empty string means success
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+template <> struct ScalarEnumerationTraits<FileKinds> {
+ static void enumeration(IO &io, FileKinds &value) {
+ io.enumCase(value, "object", fileKindObjectAtoms);
+ io.enumCase(value, "archive", fileKindArchive);
+ io.enumCase(value, "object-mach-o", fileKindObjectMachO);
+ }
+};
+
+template <> struct MappingTraits<ArchMember> {
+ static void mapping(IO &io, ArchMember &member) {
+ io.mapOptional("kind", member._kind, fileKindObjectAtoms);
+ io.mapOptional("name", member._name);
+ io.mapRequired("content", member._content);
+ }
+};
+
+// Declare that an AtomList is a yaml sequence.
+template <typename T> struct SequenceTraits<AtomList<T> > {
+ static size_t size(IO &io, AtomList<T> &seq) { return seq._atoms.size(); }
+ static T *&element(IO &io, AtomList<T> &seq, size_t index) {
+ if (index >= seq._atoms.size())
+ seq._atoms.resize(index + 1);
+ return seq._atoms[index].get();
+ }
+};
+
+// Declare that an AtomRange is a yaml sequence.
+template <typename T> struct SequenceTraits<File::AtomRange<T> > {
+ static size_t size(IO &io, File::AtomRange<T> &seq) { return seq.size(); }
+ static T *&element(IO &io, File::AtomRange<T> &seq, size_t index) {
+ assert(io.outputting() && "AtomRange only used when outputting");
+ assert(index < seq.size() && "Out of range access");
+ return seq[index].get();
+ }
+};
+
+// Used to allow DefinedAtom content bytes to be a flow sequence of
+// two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A)
+template <> struct ScalarTraits<ImplicitHex8> {
+ static void output(const ImplicitHex8 &val, void *, raw_ostream &out) {
+ uint8_t num = val;
+ out << llvm::format("%02X", num);
+ }
+
+ static StringRef input(StringRef str, void *, ImplicitHex8 &val) {
+ unsigned long long n;
+ if (getAsUnsignedInteger(str, 16, n))
+ return "invalid two-digit-hex number";
+ if (n > 0xFF)
+ return "out of range two-digit-hex number";
+ val = n;
+ return StringRef(); // returning empty string means success
+ }
+
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
+};
+
+// YAML conversion for std::vector<const lld::File*>
+template <> struct DocumentListTraits<std::vector<const lld::File *> > {
+ static size_t size(IO &io, std::vector<const lld::File *> &seq) {
+ return seq.size();
+ }
+ static const lld::File *&element(IO &io, std::vector<const lld::File *> &seq,
+ size_t index) {
+ if (index >= seq.size())
+ seq.resize(index + 1);
+ return seq[index];
+ }
+};
+
+// YAML conversion for const lld::File*
+template <> struct MappingTraits<const lld::File *> {
+ class NormArchiveFile : public lld::ArchiveLibraryFile {
+ public:
+ NormArchiveFile(IO &io) : ArchiveLibraryFile("") {}
+
+ NormArchiveFile(IO &io, const lld::File *file)
+ : ArchiveLibraryFile(file->path()), _path(file->path()) {
+ // If we want to support writing archives, this constructor would
+ // need to populate _members.
+ }
+
+ const lld::File *denormalize(IO &io) { return this; }
+
+ const AtomRange<lld::DefinedAtom> defined() const override {
+ return _noDefinedAtoms;
+ }
+
+ const AtomRange<lld::UndefinedAtom> undefined() const override {
+ return _noUndefinedAtoms;
+ }
+
+ const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
+ return _noSharedLibraryAtoms;
+ }
+
+ const AtomRange<lld::AbsoluteAtom> absolute() const override {
+ return _noAbsoluteAtoms;
+ }
+
+ void clearAtoms() override {
+ _noDefinedAtoms.clear();
+ _noUndefinedAtoms.clear();
+ _noSharedLibraryAtoms.clear();
+ _noAbsoluteAtoms.clear();
+ }
+
+ File *find(StringRef name) override {
+ for (const ArchMember &member : _members)
+ for (const lld::DefinedAtom *atom : member._content->defined())
+ if (name == atom->name())
+ return const_cast<File *>(member._content);
+ return nullptr;
+ }
+
+ std::error_code
+ parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
+ return std::error_code();
+ }
+
+ StringRef _path;
+ std::vector<ArchMember> _members;
+ };
+
+ class NormalizedFile : public lld::File {
+ public:
+ NormalizedFile(IO &io)
+ : File("", kindNormalizedObject), _io(io), _rnb(nullptr),
+ _definedAtomsRef(_definedAtoms._atoms),
+ _undefinedAtomsRef(_undefinedAtoms._atoms),
+ _sharedLibraryAtomsRef(_sharedLibraryAtoms._atoms),
+ _absoluteAtomsRef(_absoluteAtoms._atoms) {}
+
+ NormalizedFile(IO &io, const lld::File *file)
+ : File(file->path(), kindNormalizedObject), _io(io),
+ _rnb(new RefNameBuilder(*file)), _path(file->path()),
+ _definedAtomsRef(file->defined()),
+ _undefinedAtomsRef(file->undefined()),
+ _sharedLibraryAtomsRef(file->sharedLibrary()),
+ _absoluteAtomsRef(file->absolute()) {
+ }
+
+ ~NormalizedFile() override {
+ }
+
+ const lld::File *denormalize(IO &io);
+
+ const AtomRange<lld::DefinedAtom> defined() const override {
+ return _definedAtomsRef;
+ }
+
+ const AtomRange<lld::UndefinedAtom> undefined() const override {
+ return _undefinedAtomsRef;
+ }
+
+ const AtomRange<lld::SharedLibraryAtom> sharedLibrary() const override {
+ return _sharedLibraryAtomsRef;
+ }
+
+ const AtomRange<lld::AbsoluteAtom> absolute() const override {
+ return _absoluteAtomsRef;
+ }
+
+ void clearAtoms() override {
+ _definedAtoms._atoms.clear();
+ _undefinedAtoms._atoms.clear();
+ _sharedLibraryAtoms._atoms.clear();
+ _absoluteAtoms._atoms.clear();
+ }
+
+ // Allocate a new copy of this string in _storage, so the strings
+ // can be freed when File is destroyed.
+ StringRef copyString(StringRef str) {
+ char *s = _storage.Allocate<char>(str.size());
+ memcpy(s, str.data(), str.size());
+ return StringRef(s, str.size());
+ }
+
+ IO &_io;
+ std::unique_ptr<RefNameBuilder> _rnb;
+ StringRef _path;
+ AtomList<lld::DefinedAtom> _definedAtoms;
+ AtomList<lld::UndefinedAtom> _undefinedAtoms;
+ AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms;
+ AtomList<lld::AbsoluteAtom> _absoluteAtoms;
+ AtomRange<lld::DefinedAtom> _definedAtomsRef;
+ AtomRange<lld::UndefinedAtom> _undefinedAtomsRef;
+ AtomRange<lld::SharedLibraryAtom> _sharedLibraryAtomsRef;
+ AtomRange<lld::AbsoluteAtom> _absoluteAtomsRef;
+ llvm::BumpPtrAllocator _storage;
+ };
+
+ static void mapping(IO &io, const lld::File *&file) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ // Let any register tag handler process this.
+ if (info->_registry && info->_registry->handleTaggedDoc(io, file))
+ return;
+ // If no registered handler claims this tag and there is no tag,
+ // grandfather in as "!native".
+ if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map"))
+ mappingAtoms(io, file);
+ }
+
+ static void mappingAtoms(IO &io, const lld::File *&file) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedFile, const lld::File *>
+ keys(io, file, nullptr);
+ assert(info != nullptr);
+ info->_file = keys.operator->();
+
+ io.mapOptional("path", keys->_path);
+
+ if (io.outputting()) {
+ io.mapOptional("defined-atoms", keys->_definedAtomsRef);
+ io.mapOptional("undefined-atoms", keys->_undefinedAtomsRef);
+ io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtomsRef);
+ io.mapOptional("absolute-atoms", keys->_absoluteAtomsRef);
+ } else {
+ io.mapOptional("defined-atoms", keys->_definedAtoms);
+ io.mapOptional("undefined-atoms", keys->_undefinedAtoms);
+ io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms);
+ io.mapOptional("absolute-atoms", keys->_absoluteAtoms);
+ }
+ }
+
+ static void mappingArchive(IO &io, const lld::File *&file) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormArchiveFile, const lld::File *>
+ keys(io, file, &info->_file->allocator());
+
+ io.mapOptional("path", keys->_path);
+ io.mapOptional("members", keys->_members);
+ }
+};
+
+// YAML conversion for const lld::Reference*
+template <> struct MappingTraits<const lld::Reference *> {
+ class NormalizedReference : public lld::Reference {
+ public:
+ NormalizedReference(IO &io)
+ : lld::Reference(lld::Reference::KindNamespace::all,
+ lld::Reference::KindArch::all, 0),
+ _target(nullptr), _offset(0), _addend(0), _tag(0) {}
+
+ NormalizedReference(IO &io, const lld::Reference *ref)
+ : lld::Reference(ref->kindNamespace(), ref->kindArch(),
+ ref->kindValue()),
+ _target(nullptr), _targetName(targetName(io, ref)),
+ _offset(ref->offsetInAtom()), _addend(ref->addend()),
+ _tag(ref->tag()) {
+ _mappedKind.ns = ref->kindNamespace();
+ _mappedKind.arch = ref->kindArch();
+ _mappedKind.value = ref->kindValue();
+ }
+
+ const lld::Reference *denormalize(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ if (!_targetName.empty())
+ _targetName = f->copyString(_targetName);
+ DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs()
+ << "created Reference to name: '"
+ << _targetName << "' ("
+ << (const void *)_targetName.data()
+ << ", " << _targetName.size() << ")\n");
+ setKindNamespace(_mappedKind.ns);
+ setKindArch(_mappedKind.arch);
+ setKindValue(_mappedKind.value);
+ return this;
+ }
+
+ void bind(const RefNameResolver &);
+ static StringRef targetName(IO &io, const lld::Reference *ref);
+
+ uint64_t offsetInAtom() const override { return _offset; }
+ const lld::Atom *target() const override { return _target; }
+ Addend addend() const override { return _addend; }
+ void setAddend(Addend a) override { _addend = a; }
+ void setTarget(const lld::Atom *a) override { _target = a; }
+
+ const lld::Atom *_target;
+ StringRef _targetName;
+ uint32_t _offset;
+ Addend _addend;
+ RefKind _mappedKind;
+ uint32_t _tag;
+ };
+
+ static void mapping(IO &io, const lld::Reference *&ref) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys(
+ io, ref, &info->_file->allocator());
+
+ io.mapRequired("kind", keys->_mappedKind);
+ io.mapOptional("offset", keys->_offset);
+ io.mapOptional("target", keys->_targetName);
+ io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0);
+ io.mapOptional("tag", keys->_tag, 0u);
+ }
+};
+
+// YAML conversion for const lld::DefinedAtom*
+template <> struct MappingTraits<const lld::DefinedAtom *> {
+
+ class NormalizedAtom : public lld::DefinedAtom {
+ public:
+ NormalizedAtom(IO &io)
+ : _file(fileFromContext(io)), _contentType(), _alignment(1) {
+ static uint32_t ordinalCounter = 1;
+ _ordinal = ordinalCounter++;
+ }
+
+ NormalizedAtom(IO &io, const lld::DefinedAtom *atom)
+ : _file(fileFromContext(io)), _name(atom->name()),
+ _scope(atom->scope()), _interpose(atom->interposable()),
+ _merge(atom->merge()), _contentType(atom->contentType()),
+ _alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()),
+ _deadStrip(atom->deadStrip()), _dynamicExport(atom->dynamicExport()),
+ _codeModel(atom->codeModel()),
+ _permissions(atom->permissions()), _size(atom->size()),
+ _sectionName(atom->customSectionName()),
+ _sectionSize(atom->sectionSize()) {
+ for (const lld::Reference *r : *atom)
+ _references.push_back(r);
+ if (!atom->occupiesDiskSpace())
+ return;
+ ArrayRef<uint8_t> cont = atom->rawContent();
+ _content.reserve(cont.size());
+ for (uint8_t x : cont)
+ _content.push_back(x);
+ }
+
+ ~NormalizedAtom() override = default;
+
+ const lld::DefinedAtom *denormalize(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ if (!_name.empty())
+ _name = f->copyString(_name);
+ if (!_refName.empty())
+ _refName = f->copyString(_refName);
+ if (!_sectionName.empty())
+ _sectionName = f->copyString(_sectionName);
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "created DefinedAtom named: '" << _name
+ << "' (" << (const void *)_name.data()
+ << ", " << _name.size() << ")\n");
+ return this;
+ }
+
+ void bind(const RefNameResolver &);
+
+ // Extract current File object from YAML I/O parsing context
+ const lld::File &fileFromContext(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ assert(info->_file != nullptr);
+ return *info->_file;
+ }
+
+ const lld::File &file() const override { return _file; }
+ StringRef name() const override { return _name; }
+ uint64_t size() const override { return _size; }
+ Scope scope() const override { return _scope; }
+ Interposable interposable() const override { return _interpose; }
+ Merge merge() const override { return _merge; }
+ ContentType contentType() const override { return _contentType; }
+ Alignment alignment() const override { return _alignment; }
+ SectionChoice sectionChoice() const override { return _sectionChoice; }
+ StringRef customSectionName() const override { return _sectionName; }
+ uint64_t sectionSize() const override { return _sectionSize; }
+ DeadStripKind deadStrip() const override { return _deadStrip; }
+ DynamicExport dynamicExport() const override { return _dynamicExport; }
+ CodeModel codeModel() const override { return _codeModel; }
+ ContentPermissions permissions() const override { return _permissions; }
+ ArrayRef<uint8_t> rawContent() const override {
+ if (!occupiesDiskSpace())
+ return ArrayRef<uint8_t>();
+ return ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(_content.data()), _content.size());
+ }
+
+ uint64_t ordinal() const override { return _ordinal; }
+
+ reference_iterator begin() const override {
+ uintptr_t index = 0;
+ const void *it = reinterpret_cast<const void *>(index);
+ return reference_iterator(*this, it);
+ }
+ reference_iterator end() const override {
+ uintptr_t index = _references.size();
+ const void *it = reinterpret_cast<const void *>(index);
+ return reference_iterator(*this, it);
+ }
+ const lld::Reference *derefIterator(const void *it) const override {
+ uintptr_t index = reinterpret_cast<uintptr_t>(it);
+ assert(index < _references.size());
+ return _references[index];
+ }
+ void incrementIterator(const void *&it) const override {
+ uintptr_t index = reinterpret_cast<uintptr_t>(it);
+ ++index;
+ it = reinterpret_cast<const void *>(index);
+ }
+
+ void addReference(Reference::KindNamespace ns,
+ Reference::KindArch arch,
+ Reference::KindValue kindValue, uint64_t off,
+ const Atom *target, Reference::Addend a) override {
+ assert(target && "trying to create reference to nothing");
+ auto node = new (file().allocator()) SimpleReference(ns, arch, kindValue,
+ off, target, a);
+ _references.push_back(node);
+ }
+
+ const lld::File &_file;
+ StringRef _name;
+ StringRef _refName;
+ Scope _scope;
+ Interposable _interpose;
+ Merge _merge;
+ ContentType _contentType;
+ Alignment _alignment;
+ SectionChoice _sectionChoice;
+ DeadStripKind _deadStrip;
+ DynamicExport _dynamicExport;
+ CodeModel _codeModel;
+ ContentPermissions _permissions;
+ uint32_t _ordinal;
+ std::vector<ImplicitHex8> _content;
+ uint64_t _size;
+ StringRef _sectionName;
+ uint64_t _sectionSize;
+ std::vector<const lld::Reference *> _references;
+ };
+
+ static void mapping(IO &io, const lld::DefinedAtom *&atom) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedAtom, const lld::DefinedAtom *> keys(
+ io, atom, &info->_file->allocator());
+ if (io.outputting()) {
+ // If writing YAML, check if atom needs a ref-name.
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ assert(info != nullptr);
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ assert(f);
+ assert(f->_rnb);
+ if (f->_rnb->hasRefName(atom)) {
+ keys->_refName = f->_rnb->refName(atom);
+ }
+ }
+
+ io.mapOptional("name", keys->_name, StringRef());
+ io.mapOptional("ref-name", keys->_refName, StringRef());
+ io.mapOptional("scope", keys->_scope,
+ DefinedAtom::scopeTranslationUnit);
+ io.mapOptional("type", keys->_contentType,
+ DefinedAtom::typeCode);
+ io.mapOptional("content", keys->_content);
+ io.mapOptional("size", keys->_size, (uint64_t)keys->_content.size());
+ io.mapOptional("interposable", keys->_interpose,
+ DefinedAtom::interposeNo);
+ io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo);
+ io.mapOptional("alignment", keys->_alignment,
+ DefinedAtom::Alignment(1));
+ io.mapOptional("section-choice", keys->_sectionChoice,
+ DefinedAtom::sectionBasedOnContent);
+ io.mapOptional("section-name", keys->_sectionName, StringRef());
+ io.mapOptional("section-size", keys->_sectionSize, (uint64_t)0);
+ io.mapOptional("dead-strip", keys->_deadStrip,
+ DefinedAtom::deadStripNormal);
+ io.mapOptional("dynamic-export", keys->_dynamicExport,
+ DefinedAtom::dynamicExportNormal);
+ io.mapOptional("code-model", keys->_codeModel, DefinedAtom::codeNA);
+ // default permissions based on content type
+ io.mapOptional("permissions", keys->_permissions,
+ DefinedAtom::permissions(
+ keys->_contentType));
+ io.mapOptional("references", keys->_references);
+ }
+};
+
+template <> struct MappingTraits<lld::DefinedAtom *> {
+ static void mapping(IO &io, lld::DefinedAtom *&atom) {
+ const lld::DefinedAtom *atomPtr = atom;
+ MappingTraits<const lld::DefinedAtom *>::mapping(io, atomPtr);
+ atom = const_cast<lld::DefinedAtom *>(atomPtr);
+ }
+};
+
+// YAML conversion for const lld::UndefinedAtom*
+template <> struct MappingTraits<const lld::UndefinedAtom *> {
+ class NormalizedAtom : public lld::UndefinedAtom {
+ public:
+ NormalizedAtom(IO &io)
+ : _file(fileFromContext(io)), _canBeNull(canBeNullNever) {}
+
+ NormalizedAtom(IO &io, const lld::UndefinedAtom *atom)
+ : _file(fileFromContext(io)), _name(atom->name()),
+ _canBeNull(atom->canBeNull()) {}
+
+ ~NormalizedAtom() override = default;
+
+ const lld::UndefinedAtom *denormalize(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ if (!_name.empty())
+ _name = f->copyString(_name);
+
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "created UndefinedAtom named: '" << _name
+ << "' (" << (const void *)_name.data() << ", "
+ << _name.size() << ")\n");
+ return this;
+ }
+
+ // Extract current File object from YAML I/O parsing context
+ const lld::File &fileFromContext(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ assert(info->_file != nullptr);
+ return *info->_file;
+ }
+
+ const lld::File &file() const override { return _file; }
+ StringRef name() const override { return _name; }
+ CanBeNull canBeNull() const override { return _canBeNull; }
+
+ const lld::File &_file;
+ StringRef _name;
+ CanBeNull _canBeNull;
+ };
+
+ static void mapping(IO &io, const lld::UndefinedAtom *&atom) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedAtom, const lld::UndefinedAtom *> keys(
+ io, atom, &info->_file->allocator());
+
+ io.mapRequired("name", keys->_name);
+ io.mapOptional("can-be-null", keys->_canBeNull,
+ lld::UndefinedAtom::canBeNullNever);
+ }
+};
+
+template <> struct MappingTraits<lld::UndefinedAtom *> {
+ static void mapping(IO &io, lld::UndefinedAtom *&atom) {
+ const lld::UndefinedAtom *atomPtr = atom;
+ MappingTraits<const lld::UndefinedAtom *>::mapping(io, atomPtr);
+ atom = const_cast<lld::UndefinedAtom *>(atomPtr);
+ }
+};
+
+// YAML conversion for const lld::SharedLibraryAtom*
+template <> struct MappingTraits<const lld::SharedLibraryAtom *> {
+ class NormalizedAtom : public lld::SharedLibraryAtom {
+ public:
+ NormalizedAtom(IO &io)
+ : _file(fileFromContext(io)), _canBeNull(false),
+ _type(Type::Unknown), _size(0) {}
+
+ NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom)
+ : _file(fileFromContext(io)), _name(atom->name()),
+ _loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()),
+ _type(atom->type()), _size(atom->size()) {}
+
+ ~NormalizedAtom() override = default;
+
+ const lld::SharedLibraryAtom *denormalize(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ if (!_name.empty())
+ _name = f->copyString(_name);
+ if (!_loadName.empty())
+ _loadName = f->copyString(_loadName);
+
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "created SharedLibraryAtom named: '"
+ << _name << "' ("
+ << (const void *)_name.data()
+ << ", " << _name.size() << ")\n");
+ return this;
+ }
+
+ // Extract current File object from YAML I/O parsing context
+ const lld::File &fileFromContext(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ assert(info->_file != nullptr);
+ return *info->_file;
+ }
+
+ const lld::File &file() const override { return _file; }
+ StringRef name() const override { return _name; }
+ StringRef loadName() const override { return _loadName; }
+ bool canBeNullAtRuntime() const override { return _canBeNull; }
+ Type type() const override { return _type; }
+ uint64_t size() const override { return _size; }
+
+ const lld::File &_file;
+ StringRef _name;
+ StringRef _loadName;
+ ShlibCanBeNull _canBeNull;
+ Type _type;
+ uint64_t _size;
+ };
+
+ static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) {
+
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedAtom, const lld::SharedLibraryAtom *>
+ keys(io, atom, &info->_file->allocator());
+
+ io.mapRequired("name", keys->_name);
+ io.mapOptional("load-name", keys->_loadName);
+ io.mapOptional("can-be-null", keys->_canBeNull, (ShlibCanBeNull) false);
+ io.mapOptional("type", keys->_type, SharedLibraryAtom::Type::Code);
+ io.mapOptional("size", keys->_size, uint64_t(0));
+ }
+};
+
+template <> struct MappingTraits<lld::SharedLibraryAtom *> {
+ static void mapping(IO &io, lld::SharedLibraryAtom *&atom) {
+ const lld::SharedLibraryAtom *atomPtr = atom;
+ MappingTraits<const lld::SharedLibraryAtom *>::mapping(io, atomPtr);
+ atom = const_cast<lld::SharedLibraryAtom *>(atomPtr);
+ }
+};
+
+// YAML conversion for const lld::AbsoluteAtom*
+template <> struct MappingTraits<const lld::AbsoluteAtom *> {
+ class NormalizedAtom : public lld::AbsoluteAtom {
+ public:
+ NormalizedAtom(IO &io)
+ : _file(fileFromContext(io)), _scope(), _value(0) {}
+
+ NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom)
+ : _file(fileFromContext(io)), _name(atom->name()),
+ _scope(atom->scope()), _value(atom->value()) {}
+
+ ~NormalizedAtom() override = default;
+
+ const lld::AbsoluteAtom *denormalize(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ if (!_name.empty())
+ _name = f->copyString(_name);
+
+ DEBUG_WITH_TYPE("WriterYAML",
+ llvm::dbgs() << "created AbsoluteAtom named: '" << _name
+ << "' (" << (const void *)_name.data()
+ << ", " << _name.size() << ")\n");
+ return this;
+ }
+
+ // Extract current File object from YAML I/O parsing context
+ const lld::File &fileFromContext(IO &io) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ assert(info->_file != nullptr);
+ return *info->_file;
+ }
+
+ const lld::File &file() const override { return _file; }
+ StringRef name() const override { return _name; }
+ uint64_t value() const override { return _value; }
+ Scope scope() const override { return _scope; }
+
+ const lld::File &_file;
+ StringRef _name;
+ StringRef _refName;
+ Scope _scope;
+ Hex64 _value;
+ };
+
+ static void mapping(IO &io, const lld::AbsoluteAtom *&atom) {
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ MappingNormalizationHeap<NormalizedAtom, const lld::AbsoluteAtom *> keys(
+ io, atom, &info->_file->allocator());
+
+ if (io.outputting()) {
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ assert(f);
+ assert(f->_rnb);
+ if (f->_rnb->hasRefName(atom)) {
+ keys->_refName = f->_rnb->refName(atom);
+ }
+ }
+
+ io.mapRequired("name", keys->_name);
+ io.mapOptional("ref-name", keys->_refName, StringRef());
+ io.mapOptional("scope", keys->_scope);
+ io.mapRequired("value", keys->_value);
+ }
+};
+
+template <> struct MappingTraits<lld::AbsoluteAtom *> {
+ static void mapping(IO &io, lld::AbsoluteAtom *&atom) {
+ const lld::AbsoluteAtom *atomPtr = atom;
+ MappingTraits<const lld::AbsoluteAtom *>::mapping(io, atomPtr);
+ atom = const_cast<lld::AbsoluteAtom *>(atomPtr);
+ }
+};
+
+} // end namespace llvm
+} // end namespace yaml
+
+RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) {
+ typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
+ NormalizedAtom;
+ for (const lld::DefinedAtom *a : file->defined()) {
+ const auto *na = (const NormalizedAtom *)a;
+ if (!na->_refName.empty())
+ add(na->_refName, a);
+ else if (!na->_name.empty())
+ add(na->_name, a);
+ }
+
+ for (const lld::UndefinedAtom *a : file->undefined())
+ add(a->name(), a);
+
+ for (const lld::SharedLibraryAtom *a : file->sharedLibrary())
+ add(a->name(), a);
+
+ typedef MappingTraits<const lld::AbsoluteAtom *>::NormalizedAtom NormAbsAtom;
+ for (const lld::AbsoluteAtom *a : file->absolute()) {
+ const auto *na = (const NormAbsAtom *)a;
+ if (na->_refName.empty())
+ add(na->_name, a);
+ else
+ add(na->_refName, a);
+ }
+}
+
+inline const lld::File *
+MappingTraits<const lld::File *>::NormalizedFile::denormalize(IO &io) {
+ typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom
+ NormalizedAtom;
+
+ RefNameResolver nameResolver(this, io);
+ // Now that all atoms are parsed, references can be bound.
+ for (const lld::DefinedAtom *a : this->defined()) {
+ auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(a);
+ normAtom->bind(nameResolver);
+ }
+
+ return this;
+}
+
+inline void MappingTraits<const lld::DefinedAtom *>::NormalizedAtom::bind(
+ const RefNameResolver &resolver) {
+ typedef MappingTraits<const lld::Reference *>::NormalizedReference
+ NormalizedReference;
+ for (const lld::Reference *ref : _references) {
+ auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref);
+ normRef->bind(resolver);
+ }
+}
+
+inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind(
+ const RefNameResolver &resolver) {
+ _target = resolver.lookup(_targetName);
+}
+
+inline StringRef
+MappingTraits<const lld::Reference *>::NormalizedReference::targetName(
+ IO &io, const lld::Reference *ref) {
+ if (ref->target() == nullptr)
+ return StringRef();
+ YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
+ assert(info != nullptr);
+ typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile;
+ NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file);
+ RefNameBuilder &rnb = *f->_rnb;
+ if (rnb.hasRefName(ref->target()))
+ return rnb.refName(ref->target());
+ return ref->target()->name();
+}
+
+namespace lld {
+namespace yaml {
+
+class Writer : public lld::Writer {
+public:
+ Writer(const LinkingContext &context) : _ctx(context) {}
+
+ llvm::Error writeFile(const lld::File &file, StringRef outPath) override {
+ // Create stream to path.
+ std::error_code ec;
+ llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::OF_TextWithCRLF);
+ if (ec)
+ return llvm::errorCodeToError(ec);
+
+ // Create yaml Output writer, using yaml options for context.
+ YamlContext yamlContext;
+ yamlContext._ctx = &_ctx;
+ yamlContext._registry = &_ctx.registry();
+ llvm::yaml::Output yout(out, &yamlContext);
+
+ // Write yaml output.
+ const lld::File *fileRef = &file;
+ yout << fileRef;
+
+ return llvm::Error::success();
+ }
+
+private:
+ const LinkingContext &_ctx;
+};
+
+} // end namespace yaml
+
+namespace {
+
+/// Handles !native tagged yaml documents.
+class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
+ bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
+ if (io.mapTag("!native")) {
+ MappingTraits<const lld::File *>::mappingAtoms(io, file);
+ return true;
+ }
+ return false;
+ }
+};
+
+/// Handles !archive tagged yaml documents.
+class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler {
+ bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override {
+ if (io.mapTag("!archive")) {
+ MappingTraits<const lld::File *>::mappingArchive(io, file);
+ return true;
+ }
+ return false;
+ }
+};
+
+class YAMLReader : public Reader {
+public:
+ YAMLReader(const Registry ®istry) : _registry(registry) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ StringRef name = mb.getBufferIdentifier();
+ return name.endswith(".objtxt") || name.endswith(".yaml");
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const class Registry &) const override {
+ // Create YAML Input Reader.
+ YamlContext yamlContext;
+ yamlContext._registry = &_registry;
+ yamlContext._path = mb->getBufferIdentifier();
+ llvm::yaml::Input yin(mb->getBuffer(), &yamlContext);
+
+ // Fill vector with File objects created by parsing yaml.
+ std::vector<const lld::File *> createdFiles;
+ yin >> createdFiles;
+ assert(createdFiles.size() == 1);
+
+ // Error out now if there were parsing errors.
+ if (yin.error())
+ return make_error_code(lld::YamlReaderError::illegal_value);
+
+ std::shared_ptr<MemoryBuffer> smb(mb.release());
+ const File *file = createdFiles[0];
+ // Note: loadFile() should return vector of *const* File
+ File *f = const_cast<File *>(file);
+ f->setLastError(std::error_code());
+ f->setSharedMemoryBuffer(smb);
+ return std::unique_ptr<File>(f);
+ }
+
+private:
+ const Registry &_registry;
+};
+
+} // end anonymous namespace
+
+void Registry::addSupportYamlFiles() {
+ add(std::unique_ptr<Reader>(new YAMLReader(*this)));
+ add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
+ new NativeYamlIOTaggedDocumentHandler()));
+ add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
+ new ArchiveYamlIOTaggedDocumentHandler()));
+}
+
+std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) {
+ return std::unique_ptr<Writer>(new lld::yaml::Writer(context));
+}
+
+} // end namespace lld
--- /dev/null
+.globl foo
+.type foo, @object
+.size foo, 4
+foo:
+.long 1
+
+.weak bar
+.type bar, @object
+.size bar, 4
+bar:
+.long 2
--- /dev/null
+# REQUIRES: x86
+
+## Test copy relocations can be created for -no-pie and -pie.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/copy-rel.s -o %t1.o
+# RUN: ld.lld %t1.o -o %t1.so -shared -soname=so
+
+# RUN: ld.lld %t.o %t1.so -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s
+
+# RUN: ld.lld %t.o %t1.so -o %t -pie
+# RUN: llvm-readobj -r %t | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: .rela.dyn {
+# CHECK-NEXT: R_X86_64_COPY foo 0x0
+# CHECK-NEXT: R_X86_64_COPY bar 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.global _start
+_start:
+ mov $foo - ., %eax
+ movabs $bar, %rax
--- /dev/null
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/relocation-copy-alias.s -o %t2.o
+// RUN: ld.lld --hash-style=sysv -shared %t2.o -o %t.so
+// RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t3
+// RUN: llvm-readobj --dyn-symbols -r --expand-relocs %t3 | FileCheck %s
+// RUN: ld.lld --hash-style=sysv --gc-sections %t.o %t.so -o %t3
+// RUN: llvm-readobj --dyn-symbols -r --expand-relocs %t3 | FileCheck %s
+
+.global _start
+_start:
+movl $5, a1
+movl $5, b1
+movl $5, b2
+
+// CHECK: .rela.dyn {
+// CHECK-NEXT: Relocation {
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Type: R_X86_64_COPY
+// CHECK-NEXT: Symbol: a1
+// CHECK-NEXT: Addend: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Relocation {
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Type: R_X86_64_COPY
+// CHECK-NEXT: Symbol: b1
+// CHECK-NEXT: Addend: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// CHECK: Name: a1
+// CHECK-NEXT: Value: [[A:.*]]
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Binding: Global (0x1)
+// CHECK-NEXT: Type: Object (0x1)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss (0x7)
+
+// CHECK: Name: b1
+// CHECK-NEXT: Value: [[B:.*]]
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object (0x1)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+
+// CHECK: Name: b2
+// CHECK-NEXT: Value: [[B]]
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object (0x1)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+
+// CHECK: Name: a2
+// CHECK-NEXT: Value: [[A]]
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Binding: Weak
+// CHECK-NEXT: Type: Object (0x1)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
+
+// CHECK: Name: b3
+// CHECK-NEXT: Value: [[B]]
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Object (0x1)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .bss
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0
+ content: [ 0xC3 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ ]
+ value: 0
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ ]
+install-name: /usr/lib/libSystem.B.dylib
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55 ]
+
+global-symbols:
+ - name: dyld_stub_binder
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -lto_library %t -print-atoms -r %s 2>&1 | FileCheck %s
+#
+# Test that the -lto_library option does not result in an error.
+#
+
+# CHECK-NOT: -lto_library
+
+--- !native
+defined-atoms:
+ - name: _foo
+...
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc %s 2>&1 | FileCheck %s
+#
+# Test that the -objc_gc is rejected.
+#
+
+# CHECK: error: -objc_gc is not supported
+
+--- !native
+defined-atoms:
+ - name: _main
+ type: code
+ scope: global
+ content: [ 0x90 ]
+
+...
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc_compaction %s 2>&1 | FileCheck %s
+#
+# Test that the -objc_gc_compaction is rejected.
+#
+
+# CHECK: error: -objc_gc_compaction is not supported
+
+--- !native
+defined-atoms:
+ - name: _main
+ type: code
+ scope: global
+ content: [ 0x90 ]
+
+...
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -objc_gc_only %s 2>&1 | FileCheck %s
+#
+# Test that the -objc_gc_only is rejected.
+#
+
+# CHECK: error: -objc_gc_only is not supported
+
+--- !native
+defined-atoms:
+ - name: _main
+ type: code
+ scope: global
+ content: [ 0x90 ]
+
+...
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/native-and-mach-o.objtxt \
+# RUN: %p/Inputs/native-and-mach-o2.objtxt -o %t && \
+# RUN: llvm-nm %t | FileCheck %s
+#
+# Test a mix of atoms and mach-o both encoded in yaml
+#
+
+--- !native
+defined-atoms:
+ - name: _main
+ type: code
+ scope: global
+ content: [ 55, 48, 89, E5, 30, C0, E8, 00,
+ 00, 00, 00, 31, C0, 5D, C3 ]
+ references:
+ - offset: 7
+ kind: branch32
+ target: _foo
+
+undefined-atoms:
+ - name: _foo
+
+...
+
+# CHECK: {{[0-9a-f]+}} T _foo
+# CHECK: {{[0-9a-f]+}} T _main
--- /dev/null
+# -*- Python -*-
+
+
+#
+# Dump out Xcode binary dependency file.
+#
+
+import sys
+
+f = open(sys.argv[1], "rb")
+byte = f.read(1)
+while byte != b'':
+ if byte == b'\000':
+ sys.stdout.write("linker-vers: ")
+ elif byte == b'\020':
+ sys.stdout.write("input-file: ")
+ elif byte == b'\021':
+ sys.stdout.write("not-found: ")
+ elif byte == b'\100':
+ sys.stdout.write("output-file: ")
+ byte = f.read(1)
+ while byte != b'\000':
+ if byte != b'\012':
+ sys.stdout.write(byte.decode("ascii"))
+ byte = f.read(1)
+ sys.stdout.write("\n")
+ byte = f.read(1)
+
+f.close()
+
--- /dev/null
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000000' ]
+platform: macosx
+install-name: '/usr/lib/libSystem.B.dylib'
+current-version: 0001.001.1
+exports:
+ - archs: [ 'x86_64' ]
+ re-exports: [ '/usr/lib/system/libdyld.dylib',
+ '/usr/lib/system/libsystem_c.dylib',
+ '/usr/lib/system/libsystem_m.dylib' ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000001' ]
+platform: macosx
+install-name: '/usr/lib/system/libdyld.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ dyld_stub_binder ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000002' ]
+platform: macosx
+install-name: '/usr/lib/system/libsystem_c.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ ]
+--- !tapi-tbd-v3
+archs: [ x86_64 ]
+uuids: [ 'x86_64: 00000000-0000-0000-0000-000000000003' ]
+platform: macosx
+install-name: '/usr/lib/system/libsystem_m.dylib'
+current-version: 0001.001.1
+parent-umbrella: System
+exports:
+ - archs: [ 'x86_64' ]
+ symbols: [ ___nan ]
+...
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
--- /dev/null
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFE, 0xFF, 0xFF, 0xEB, 0x02, 0x00, 0x00, 0xFA,
+ 0xFC, 0xFF, 0xFF, 0xEB, 0xFB, 0xFF, 0xFF, 0xFA,
+ 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1 ]
+ relocations:
+ - offset: 0x0000000C
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000004
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000018
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000004
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 3
+local-symbols:
+ - name: _d2
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000018
+global-symbols:
+ - name: _a1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _a2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000014
+undefined-symbols:
+ - name: _t1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
--- /dev/null
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7,
+ 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3,
+ 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ]
+ relocations:
+ - offset: 0x00000014
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000010
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000002
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _a2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: _t2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _a1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
--- /dev/null
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: arm64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
--- /dev/null
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
--- /dev/null
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3 ]
+global-symbols:
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __objc_methname
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000000
+ content: [ 0x61, 0x62, 0x63, 0x00 ]
+ - segment: __TEXT
+ section: __objc_classname
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000006
+ content: [ 0x61, 0x62, 0x63, 0x00 ]
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x000000000000000A
+ content: [ 0x61, 0x62, 0x63, 0x00 ]
--- /dev/null
+#
+# For use with exported_symbols_list.yaml
+#
+_foo
+_b
+
--- /dev/null
+/foo/bar/a.o
+/foo/bar/b.o
+/foo/x.a
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x0D, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x8B, 0x00, 0x03, 0x01, 0x48, 0x8B,
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000019
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x0000000E
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000007
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _zazzle
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libfoobar.dylib
+exports:
+ - name: _bar
+ - name: _zazzle
+ - name: _foo
+ - name: _aaa
+ - name: _fff
+ - name: _zzz
--- /dev/null
+--- !mach-o
+arch: arm64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _fprintf
+ - name: ___stdoutp
+ - name: dyld_stub_binder
--- /dev/null
+--- !mach-o
+arch: armv6
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _printf
+ - name: dyld_stub_binder
--- /dev/null
+--- !mach-o
+arch: armv7
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _printf
+ - name: dyld_stub_binder
--- /dev/null
+--- !mach-o
+arch: x86
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _printf
+ - name: dyld_stub_binder
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _fprintf
+ - name: dyld_stub_binder
+ - name: ___stdoutp
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: _open
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libfoo.dylib
+compat-version: 2.0
+current-version: 3.4
+exports:
+ - name: _foo
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libbaz.dylib
+compat-version: 3.0
+current-version: 4.5
+exports:
+ - name: _baz
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libbar.dylib
+compat-version: 1.0
+current-version: 2.3
+exports:
+ - name: _bar
--- /dev/null
+
+# This object file has no version min and so will prevent any -r link from emitting
+# a version min.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
--- /dev/null
+
+# input file for order_file-basic.yaml
+
+_func2
+libfoo.a(foo.o):_foo # tests file specific ordering within archive
+i386:_func3 # wrong arch, so ignored
+armv7:_func3 # wrong arch, so ignored
+_func1
+_notfound # unknown symbol silently ignored
+_data3 # data symbols should be orderable
+
--- /dev/null
+bar/a.o
+bar/b.o
+x.a
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ MH_TWOLEVEL ]
+install-name: /junk/libfoo.dylib
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000F9A
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000F9A
+dependents:
+ - path: /junk/libbar.dylib
+ kind: LC_REEXPORT_DYLIB
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ MH_TWOLEVEL ]
+install-name: /junk/libbar.dylib
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000F9A
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ]
+global-symbols:
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000F9A
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ MH_TWOLEVEL ]
+install-name: /usr/lib/libSystem.B.dylib
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55 ]
+
+global-symbols:
+ - name: dyld_stub_binder
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s %p/Inputs/hello-world-x86_64.yaml 2>&1 | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 ]
+...
--- /dev/null
+--- !mach-o
+arch: arm64
+file-type: MH_DYLIB
+install-name: /usr/lib/libc++.dylib
+exports:
+ - name: __Unwind_Resume
+ - name: __ZTIl
+ - name: __ZTIi
+ - name: ___cxa_end_catch
+ - name: ___cxa_begin_catch
+ - name: ___cxa_allocate_exception
+ - name: ___cxa_throw
+ - name: ___gxx_personality_v0
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00,
+ 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00,
+ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _myGlobal
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90,
+ 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3,
+ 0x31, 0xC0, 0xC3 ]
+local-symbols:
+ - name: _myStatic
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000B
+ - name: _myVariablePreviouslyKnownAsPrivateExtern
+ type: N_SECT
+ scope: [ N_PEXT ]
+ sect: 1
+ desc: [ N_SYMBOL_RESOLVER ]
+ value: 0x0000000000000011
+global-symbols:
+ - name: _myGlobal
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myGlobalWeak
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000002
+ - name: _myHidden
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000004
+ - name: _myHiddenWeak
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000007
+ - name: _myResolver
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_SYMBOL_RESOLVER ]
+ value: 0x0000000000000010
+
+install-name: libspecial.dylib
--- /dev/null
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+flags: [ ]
+install-name: /usr/lib/libSystem.B.dylib
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55 ]
+
+global-symbols:
+ - name: dyld_stub_binder
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s 2> %t.err
+# RUN: FileCheck %s < %t.err
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xC3 ]
+
+global-symbols:
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
--- /dev/null
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
--- /dev/null
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/PIE.yaml -o %t && \
+# RUN: llvm-objdump --macho --private-headers %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/PIE.yaml -pie -o %t\
+# RUN: && llvm-objdump --macho --private-headers %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/PIE.yaml -no_pie -o %t\
+# RUN: && llvm-objdump --macho --private-headers %t \
+# RUN: | FileCheck --check-prefix=CHECK_NO_PIE %s
+#
+# Test various PIE options.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+...
+
+# CHECK: MH_MAGIC_64 {{[0-9a-zA-Z _]+}} TWOLEVEL PIE
+# CHECK_NO_PIE-NOT: MH_MAGIC_64 {{[0-9a-zA-Z _]+}} TWOLEVEL PIE
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -r %t -o %t2 -print_atoms | FileCheck %s
+#
+# Test that alignment info round trips through -r
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x90, 0x90, 0x90, 0xC3, 0xC3, 0xC3 ]
+local-symbols:
+ - name: _f1
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000003
+ - name: _f2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000004
+ - name: _f3
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000005
+...
+
+# CHECK: defined-atoms:
+# CHECK: - content: [ 90, 90, 90 ]
+# CHECK: alignment: 16
+# CHECK: - name: _f1
+# CHECK: content: [ C3 ]
+# CHECK: alignment: 3 mod 16
+# CHECK: - name: _f2
+# CHECK: content: [ C3 ]
+# CHECK: alignment: 4 mod 16
+# CHECK: - name: _f3
+# CHECK: content: [ C3 ]
+# CHECK: alignment: 5 mod 16
--- /dev/null
+# REQUIRES: arm
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch armv7 -dylib -print_atoms %t -o %t2 \
+# RUN: %p/Inputs/armv7/libSystem.yaml -sectalign __TEXT __text 0x1000 | FileCheck %s
+# RUN: llvm-objdump -d --macho --no-symbolic-operands %t2 | FileCheck --check-prefix=CODE %s
+#
+# Test thumb and arm branches round trip through -r.
+# Test movw/movt pairs have low bit set properly for thumb vs arm.
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x40, 0xF2, 0x25, 0x00, 0xC0, 0xF2, 0x00, 0x00,
+ 0x40, 0xF2, 0x01, 0x01, 0xC0, 0xF2, 0x00, 0x01,
+ 0x40, 0xF2, 0x4E, 0x02, 0xC0, 0xF2, 0x00, 0x02,
+ 0x40, 0xF2, 0x2A, 0x03, 0xC0, 0xF2, 0x00, 0x03,
+ 0x78, 0x44, 0x70, 0x47, 0x70, 0x47, 0x25, 0x00,
+ 0x00, 0xE3, 0x00, 0x00, 0x40, 0xE3, 0xD7, 0x1F,
+ 0x0F, 0xE3, 0xFF, 0x1F, 0x4F, 0xE3, 0x4E, 0x20,
+ 0x00, 0xE3, 0x00, 0x20, 0x40, 0xE3, 0x00, 0x30,
+ 0x00, 0xE3, 0x00, 0x30, 0x40, 0xE3, 0x0F, 0x00,
+ 0x80, 0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF,
+ 0x2F, 0xE1 ]
+ relocations:
+ - offset: 0x00000042
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 1
+ pc-rel: false
+ value: 0x0000004E
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ value: 0x00000046
+ - offset: 0x0000003E
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 0
+ pc-rel: false
+ value: 0x0000004E
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ value: 0x00000046
+ - offset: 0x0000003A
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000004E
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000036
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000032
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 1
+ pc-rel: false
+ value: 0x00000024
+ - offset: 0x0000FFD6
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ value: 0x00000046
+ - offset: 0x0000002E
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 0
+ pc-rel: false
+ value: 0x00000024
+ - offset: 0x0000FFFF
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ value: 0x00000046
+ - offset: 0x0000002A
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000025
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000026
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000001C
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x0000004E
+ - offset: 0x0000002A
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000018
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x0000004E
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000014
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000004E
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000010
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000000C
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x00000024
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000008
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000024
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000004
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000025
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000000
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+local-symbols:
+ - name: _t1
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000024
+ - name: _a2
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000004E
+ - name: _a1
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000026
+...
+
+# CHECK: defined-atoms:
+# CHECK: - name: _t1
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t1
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 4
+# CHECK: target: _t2
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw_funcRel
+# CHECK: offset: 8
+# CHECK: target: _t2
+# CHECK: addend: -36
+# CHECK: - kind: thumb_movt_funcRel
+# CHECK: offset: 12
+# CHECK: target: _t2
+# CHECK: addend: -36
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 16
+# CHECK: target: _a2
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 20
+# CHECK: target: _a2
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw_funcRel
+# CHECK: offset: 24
+# CHECK: target: _a2
+# CHECK: addend: -36
+# CHECK: - kind: thumb_movt_funcRel
+# CHECK: offset: 28
+# CHECK: target: _a2
+# CHECK: addend: -36
+# CHECK: - name: _t2
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK: - name: _a1
+# CHECK: references:
+# CHECK: - kind: arm_movw
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movt
+# CHECK: offset: 4
+# CHECK: target: _t2
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movw_funcRel
+# CHECK: offset: 8
+# CHECK: target: _t2
+# CHECK: addend: -40
+# CHECK: - kind: arm_movt_funcRel
+# CHECK: offset: 12
+# CHECK: target: _t2
+# CHECK: addend: -40
+# CHECK: - kind: arm_movw
+# CHECK: offset: 16
+# CHECK: target: _a2
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movt
+# CHECK: offset: 20
+# CHECK: target: _a2
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movw_funcRel
+# CHECK: offset: 24
+# CHECK: target: _a2
+# CHECK: addend: -40
+# CHECK: - kind: arm_movt_funcRel
+# CHECK: offset: 28
+# CHECK: target: _a2
+# CHECK: addend: -40
+# CHECK: - name: _a2
+
+
+# CODE: _t1:
+# CODE-NEXT: movw r0, #4133
+# CODE-NEXT: movt r0, #0
+# CODE-NEXT: movw r1, #1
+# CODE-NEXT: movt r1, #0
+# CODE-NEXT: movw r2, #4174
+# CODE-NEXT: movt r2, #0
+# CODE-NEXT: movw r3, #42
+# CODE-NEXT: movt r3, #0
+
+
+# CODE: _a1:
+# CODE-NEXT: movw r0, #4133
+# CODE-NEXT: movt r0, #0
+# CODE-NEXT: movw r1, #65495
+# CODE-NEXT: movt r1, #65535
+# CODE-NEXT: movw r2, #4174
+# CODE-NEXT: movt r2, #0
+# CODE-NEXT: movw r3, #0
+# CODE-NEXT: movt r3, #0
+
+
+
+# .syntax unified
+# .align 2
+#
+# .code 16
+# .thumb_func _t1
+#_t1:
+# movw r0, :lower16:(_t2)
+# movt r0, :upper16:(_t2)
+# movw r1, :lower16:(_t2-(L0+4))
+# movt r1, :upper16:(_t2-(L0+4))
+# movw r2, :lower16:(_a2)
+# movt r2, :upper16:(_a2)
+# movw r3, :lower16:(_a2-(L0+4))
+# movt r3, :upper16:(_a2-(L0+4))
+#L0:
+# add r0, pc
+# bx lr
+#
+#
+# .code 16
+# .thumb_func _t2
+#_t2:
+# bx lr
+#
+#
+#
+# .code 32
+#_a1:
+# movw r0, :lower16:(_t2)
+# movt r0, :upper16:(_t2)
+# movw r1, :lower16:(_t2-(L1+8))
+# movt r1, :upper16:(_t2-(L1+8))
+# movw r2, :lower16:(_a2)
+# movt r2, :upper16:(_a2)
+# movw r3, :lower16:(_a2-(L1+8))
+# movt r3, :upper16:(_a2-(L1+8))
+#L1:
+# add r0, pc
+# bx lr
+#
+#_a2:
+# bx lr
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %s \
+# RUN: %p/Inputs/arm-interworking.yaml -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch armv7 -dylib -print_atoms \
+# RUN: %p/Inputs/armv7/libSystem.yaml %t -o %t2 | FileCheck %s \
+# RUN: && llvm-readobj -S --section-data %t2 | FileCheck -check-prefix=CODE %s
+#
+# Test thumb and arm branches round trip through -r.
+# Test bl/blx instructions are fixed up properly.
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFF, 0xF7, 0xFE, 0xFF, 0xC0, 0x46, 0xFF, 0xF7,
+ 0xFC, 0xEF, 0xC0, 0x46, 0xFF, 0xF7, 0xF8, 0xEF,
+ 0xFF, 0xF7, 0xF6, 0xFF, 0xC0, 0x46, 0xFF, 0xF7,
+ 0xF3, 0xFF, 0xC0, 0x46, 0x00, 0xF0, 0x06, 0xE8,
+ 0xC0, 0x46, 0x00, 0xF0, 0x03, 0xF8, 0x00, 0xF0,
+ 0x02, 0xF8, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47 ]
+ relocations:
+ - offset: 0x00000026
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000022
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x0000001C
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000016
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000010
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x0000000C
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000000
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000030
+ content: [ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000004
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+local-symbols:
+ - name: _t3
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x000000000000002E
+ - name: _d1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000030
+global-symbols:
+ - name: _t1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x000000000000002C
+undefined-symbols:
+ - name: _a1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _a2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: defined-atoms:
+# CHECK: - name: _d1
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _a1
+# CHECK: - name: _d2
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: _t1
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _a1
+# CHECK: - name: _t1
+# CHECK: scope: global
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t1
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 0
+# CHECK: target: _a1
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 6
+# CHECK: target: _a2
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 12
+# CHECK: target: _a2
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 16
+# CHECK: target: _t1
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 22
+# CHECK: target: _t1
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 28
+# CHECK: target: _t2
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 34
+# CHECK: target: _t2
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 38
+# CHECK: target: _t3
+# CHECK: - name: _t2
+# CHECK: scope: global
+# CHECK: content: [ 70, 47 ]
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK: - name: _t3
+# CHECK: content: [ 70, 47 ]
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t3
+# CHECK: - name: _a1
+# CHECK: scope: global
+# CHECK: references:
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 0
+# CHECK: target: _a1
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 4
+# CHECK: target: _a2
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 8
+# CHECK: target: _t1
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 12
+# CHECK: target: _t2
+# CHECK: - name: _a2
+# CHECK: scope: global
+
+# CODE: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
+# CODE: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
+# CODE: SectionData (
+# CODE: 0000: 00F016E8 C04600F0 1EE8C046 00F01AE8
+# CODE: 0010: FFF7F6FF C046FFF7 F3FFC046 00F006F8
+# CODE: 0020: C04600F0 03F800F0 02F87047 70477047
+# CODE: 0030: FEFFFFEB 020000EB F0FFFFFA FAFFFFFA
+# CODE: 0040: 1EFF2FE1 1EFF2FE1
+# CODE: )
+
+# CODE: Name: __data (5F 5F 64 61 74 61 00 00 00 00 00 00 00 00 00 00)
+# CODE: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00)
+# CODE: SectionData (
+# CODE: 0000: E50F0000 E80F0000 B90F0000 E80F0000
+# CODE: )
+
+# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic.
+# Verify the low (thumb) bit is set on the first and third pointers but not the second and fourth.
+
+
+
+# Input file one:
+#
+# .align 2
+# .code 16
+# .globl _t1
+# .thumb_func _t1
+#_t1:
+# bl _a1
+# nop
+# blx _a2
+# nop
+# blx _a2
+# bl _t1
+# nop
+# bl _t1
+# nop
+# blx _t2
+# nop
+# blx _t2
+# bx lr
+#
+# .globl _t2
+# .thumb_func _t2
+#_t2:
+# bx lr
+#
+# .data
+#_d1: .long _t2
+# .long _a1
+
+
+
+# Input file two:
+#
+# .align 2
+# .code 32
+# .globl _a1
+#_a1:
+# bl _a1
+# blx _a2
+# bl _t1
+# blx _t2
+# bx lr
+#
+# .globl _a2
+#_a2:
+# bx lr
+#
+# .data
+#_d2: .long _t1
+# .long _a1
+
+
+
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 %s %p/Inputs/arm-shims.yaml \
+# RUN: -dylib %p/Inputs/armv7/libSystem.yaml -o %t
+# RUN: llvm-readobj -S --section-data %t | FileCheck %s
+#
+# Test b from arm to thumb or vice versa has shims added.s
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7,
+ 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3,
+ 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ]
+ relocations:
+ - offset: 0x00000014
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000010
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000002
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _a1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: _t1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _a2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: Section {
+# CHECK: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
+# CHECK: SectionData (
+# CHECK: 0000: 00BF00F0 10E800F0 19B80000 00F020E3
+# CHECK: 0010: 000000FA 0F0000EA 00BFFFF7 F8EF00F0
+# CHECK: 0020: 07B80000 00F020E3 F4FFFFFA 050000EA
+# CHECK: 0030: DFF804C0 FF446047 D4FFFFFF DFF804C0
+# CHECK: 0040: FF446047 E0FFFFFF 04C09FE5 0CC08FE0
+# CHECK: 0050: 1CFF2FE1 ADFFFFFF 04C09FE5 0CC08FE0
+# CHECK: 0060: 1CFF2FE1 B5FFFFFF
+# CHECK: )
+
+# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic.
+
+
+# Input file one:
+#
+# .align 2
+# .code 16
+# .globl _t1
+# .thumb_func _t1
+#_t1:
+# nop
+# blx _a2
+# b _a2
+#
+# .code 32
+# .align 2
+# .globl _a1
+#_a1:
+# nop
+# blx _t2
+# b _t2
+
+
+
+# Input file two:
+#
+# .align 2
+# .code 16
+# .globl _t2
+# .thumb_func _t2
+#_t2:
+# nop
+# blx _a1
+# b _a1
+#
+# .code 32
+# .align 2
+# .globl _a2
+#_a2:
+# nop
+# blx _t1
+# b _t1
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 %s -r -print_atoms -o %t | FileCheck %s
+#
+# Test that assembly written without .subsections_via_symbols is parsed so
+# that atoms are non-dead-strip and there is a layout-after references
+# chaining atoms together.
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x04, 0x10, 0x9F, 0xE5, 0x04, 0x20, 0x9F, 0xE5,
+ 0x1E, 0xFF, 0x2F, 0xE1, 0x78, 0x56, 0x34, 0x12,
+ 0x21, 0x43, 0x65, 0x87 ]
+local-symbols:
+ - name: constants1
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000C
+ - name: constants2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000010
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - name: _foo
+# CHECK: scope: global
+# CHECK: content: [ 04, 10, 9F, E5, 04, 20, 9F, E5, 1E, FF, 2F, E1 ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: layout-after
+# CHECK: offset: 0
+# CHECK: target: constants1
+# CHECK: - name: constants1
+# CHECK: content: [ 78, 56, 34, 12 ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: layout-after
+# CHECK: offset: 0
+# CHECK: target: constants2
+# CHECK: - name: constants2
+# CHECK: content: [ 21, 43, 65, 87 ]
+# CHECK: dead-strip: never
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r %s -o %t
+# RUN: ld64.lld.darwinold -arch arm64 -r %t -o %t2
+# RUN: llvm-objdump -s --section="__eh_frame" %t | FileCheck %s
+# RUN: llvm-objdump -s --section="__eh_frame" %t2 | FileCheck %s
+
+# The reference from FDE->CIE is implicitly created as a negDelta32.
+# We don't emit these in to the binary as relocations, so we need to
+# make sure that the offset in the FDE to the CIE is the correct value.
+# CHECK: {{[0-9abcdef]*}} 10000000 00000000 017a5200 01781e01
+# CHECK: {{[0-9abcdef]*}} 100c1f00 20000000 18000000 b8ffffff
+# Note, this one that matters ^~~~~~~~
+# It needs to be 0x18 as that is the offset back to 0 where the CIE is.
+# CHECK: {{[0-9abcdef]*}} ffffffff 20000000 00000000 00480e10
+# CHECK: {{[0-9abcdef]*}} 9e019d02 00000000
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x80, 0x52,
+ 0xFD, 0x7B, 0xC1, 0xA8, 0xC0, 0x03, 0x5F, 0xD6 ]
+ relocations:
+ - offset: 0x00000010
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 6
+ - offset: 0x0000000C
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000008
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000020
+ content: [ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F,
+ 0x72, 0x6C, 0x64, 0x00 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000030
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000050
+ content: [ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x1E, 0x01,
+ 0x10, 0x0C, 0x1F, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0E, 0x10,
+ 0x9E, 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: L_str
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000020
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000020
+ - name: ltmp2
+ type: N_SECT
+ sect: 3
+ value: 0x0000000000000030
+ - name: ltmp3
+ type: N_SECT
+ sect: 4
+ value: 0x0000000000000050
+global-symbols:
+ - name: __Z3fooi
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _puts
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch arm64 %s -r \
+# RUN: 2> %t.err
+# RUN: FileCheck %s < %t.err
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xFF, 0x83, 0x00, 0xD1, 0xE0, 0x0B, 0x00, 0xF9,
+ 0x08, 0x00, 0x40, 0xB9, 0x08, 0x0D, 0x00, 0x71,
+ 0x08, 0x09, 0x00, 0x71, 0xE8, 0x0F, 0x00, 0xB9,
+ 0xC8, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x14,
+ 0xE8, 0x03, 0x00, 0x32, 0x08, 0x01, 0x00, 0x12,
+ 0xE8, 0x7F, 0x00, 0x39, 0x02, 0x00, 0x00, 0x14 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x000000000001C348
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+
+# Make sure that the offsets of the subtractor and unsigned both match.
+# CHECK: bad relocation (paired relocs must have the same offset) in section __DATA/__data (r1_address=1, r1_type=1, r1_extern=1, r1_length=3, r1_pcrel=0, r1_symbolnum=1), (r2_address=0, r2_type=0, r2_extern=1, r2_length=3, r2_pcrel=0, r2_symbolnum=1)
+ - offset: 0x00000001
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _f1
+ type: N_SECT
+ sect: 2
+ value: 0x000000000001C348
+ - name: _f2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000010
+ - name: _f3
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000020
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %s -o %t
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %t -o %t2
+# RUN: llvm-objdump --section-headers %t | FileCheck %s
+# RUN: llvm-objdump --section-headers %t2 | FileCheck %s
+
+# Make sure that the sections are sorted. Currently we want this order:
+# __text, __unwind_info
+
+# CHECK: Sections:
+# CHECK: 0 __text {{.*}} TEXT
+# CHECK: 1 __compact_unwind {{.*}}
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 8
+ address: 0x0000000000000000
+ content: [ 0xC0, 0x03, 0x5F, 0xD6, 0xC0, 0x03, 0x5F, 0xD6 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000008
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000020
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+global-symbols:
+ - name: __Z3fooi
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: __Z4foo2i
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000004
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t
+# RUN: obj2yaml %t | FileCheck %s
+#
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x08, 0x00, 0x00, 0x90, 0x08, 0x01, 0x40, 0xF9,
+ 0x00, 0x01, 0x40, 0xF9, 0x01, 0x00, 0x00, 0x90,
+ 0x21, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x94,
+ 0x00, 0x00, 0x80, 0x52, 0xFD, 0x7B, 0xC1, 0xA8,
+ 0xC0, 0x03, 0x5F, 0xD6 ]
+ relocations:
+ - offset: 0x0000001C
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000018
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000014
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000C
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x000000000000002C
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: l_.str
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: ___stdoutp
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _fprintf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: BindOpcodes:
+# CHECK: - Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: dyld_stub_binder
+# CHECK: - Opcode: BIND_OPCODE_SET_TYPE_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData: [ 0x0 ]
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: ___stdoutp
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData: [ 0x10 ]
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+
+# CHECK: LazyBindOpcodes:
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData: [ 0x18 ]
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: _fprintf
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s
+#
+# Test -keep_private_externs in -r mode.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __objc_methname
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000000
+ content: [ 0x61, 0x62, 0x63, 0x00, 0x64, 0x65, 0x66, 0x00 ]
+ - segment: __TEXT
+ section: __objc_classname
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000006
+ content: [ 0x61, 0x62, 0x63, 0x00, 0x67, 0x68, 0x69, 0x00 ]
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x000000000000000A
+ content: [ 0x61, 0x62, 0x63, 0x00, 0x6A, 0x6B, 0x6C, 0x00 ]
+
+
+...
+
+# CHECK: defined-atoms:
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 61, 62, 63, 00 ]
+# CHECK: merge: by-content
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__TEXT/__objc_methname'
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 64, 65, 66, 00 ]
+# CHECK: merge: by-content
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__TEXT/__objc_methname'
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 61, 62, 63, 00 ]
+# CHECK: merge: by-content
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__TEXT/__objc_classname'
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 67, 68, 69, 00 ]
+# CHECK: merge: by-content
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__TEXT/__objc_classname'
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 61, 62, 63, 00 ]
+# CHECK: merge: by-content
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 6A, 6B, 6C, 00 ]
+# CHECK: merge: by-content
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -data_in_code_info && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_data_in_code_info && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -data_in_code_info -no_data_in_code_info && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r -data_in_code_info && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r -no_data_in_code_info && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: Load command {{[0-9]*}}
+# CHECK: cmd LC_DATA_IN_CODE
+# CHECK: cmdsize 16
+# CHECK: dataoff
+# CHECK: datasize
+
+# NO_DATA_IN_CODE_INFO-NOT: LC_DATA_IN_CODE
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm %t | FileCheck %s
+#
+# Test that a data-only dylib can be built.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _myData
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: _myData
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -dead_strip -export_dynamic %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
+# RUN: ld64.lld.darwinold -arch x86_64 -export_dynamic -dead_strip %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
+# RUN: ld64.lld.darwinold -arch x86_64 -dead_strip %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t2.dylib -print_atoms | FileCheck -check-prefix=CHECK2 %s
+
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t3.o
+# RUN: llvm-nm -m %t3.o | FileCheck -check-prefix=RELOCATABLE_SYMBOLS %s
+
+#
+# Test that -export_dynamic -dead-strip from removing globals.
+#
+
+---
+defined-atoms:
+ - name: def
+ scope: global
+ dead-strip: never
+ - name: dead
+ scope: global
+shared-library-atoms:
+ - name: dyld_stub_binder
+ load-name: /usr/lib/libSystem.B.dylib
+ type: unknown
+...
+
+# CHECK1: name: def
+# CHECK1: name: dead
+
+# CHECK2: name: def
+# CHECK2-NOT: name: dead
+
+# RELOCATABLE_SYMBOLS: external def
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -o %t %s -dylib %p/Inputs/x86_64/libSystem.yaml && \
+# RUN: llvm-nm --no-sort --debug-syms %t | FileCheck %s
+
+# CHECK: 0000000000000000 - 00 0000 SO /Users/lhames/Projects/lld/lld-svn-tot/scratch/
+# CHECK-NEXT: 0000000000000000 - 00 0000 SO hw.c
+# CHECK-NEXT: {{[0-9a-f]+}} - 03 0001 OSO {{.*}}{{/|\\}}test{{/|\\}}mach-o{{/|\\}}debug-syms.yaml
+# CHECK-NEXT: 0000000000000fa0 - 01 0000 BNSYM
+# CHECK-NEXT: 0000000000000fa0 - 01 0000 FUN _main
+# CHECK-NEXT: 0000000000000016 - 00 0000 FUN
+# CHECK-NEXT: 0000000000000016 - 01 0000 ENSYM
+# CHECK-NEXT: 0000000000000000 - 01 0000 SO
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+min-os-version-kind: LC_VERSION_MIN_MACOSX
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0xC7, 0x45,
+ 0xFC, 0x00, 0x00, 0x00, 0x00, 0x89, 0x7D, 0xF8,
+ 0x48, 0x89, 0x75, 0xF0, 0x5D, 0xC3 ]
+ - segment: __DWARF
+ section: __debug_str
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x0000000000000016
+ content: [ 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x4C, 0x4C,
+ 0x56, 0x4D, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6F, 0x6E, 0x20, 0x38, 0x2E, 0x30, 0x2E, 0x30,
+ 0x20, 0x28, 0x63, 0x6C, 0x61, 0x6E, 0x67, 0x2D,
+ 0x38, 0x30, 0x30, 0x2E, 0x30, 0x2E, 0x32, 0x34,
+ 0x2E, 0x31, 0x29, 0x00, 0x68, 0x77, 0x2E, 0x63,
+ 0x00, 0x2F, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2F,
+ 0x6C, 0x68, 0x61, 0x6D, 0x65, 0x73, 0x2F, 0x50,
+ 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x73, 0x2F,
+ 0x6C, 0x6C, 0x64, 0x2F, 0x6C, 0x6C, 0x64, 0x2D,
+ 0x73, 0x76, 0x6E, 0x2D, 0x74, 0x6F, 0x74, 0x2F,
+ 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x00,
+ 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x69, 0x6E, 0x74,
+ 0x00, 0x61, 0x72, 0x67, 0x63, 0x00, 0x61, 0x72,
+ 0x67, 0x76, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00 ]
+ - segment: __DWARF
+ section: __debug_loc
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000008E
+ - segment: __DWARF
+ section: __debug_abbrev
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000008E
+ content: [ 0x01, 0x11, 0x01, 0x25, 0x0E, 0x13, 0x05, 0x03,
+ 0x0E, 0x10, 0x06, 0x1B, 0x0E, 0x11, 0x01, 0x12,
+ 0x01, 0x00, 0x00, 0x02, 0x2E, 0x01, 0x11, 0x01,
+ 0x12, 0x01, 0x40, 0x0A, 0x03, 0x0E, 0x3A, 0x0B,
+ 0x3B, 0x0B, 0x27, 0x0C, 0x49, 0x13, 0x3F, 0x0C,
+ 0x00, 0x00, 0x03, 0x05, 0x00, 0x02, 0x0A, 0x03,
+ 0x0E, 0x3A, 0x0B, 0x3B, 0x0B, 0x49, 0x13, 0x00,
+ 0x00, 0x04, 0x24, 0x00, 0x03, 0x0E, 0x3E, 0x0B,
+ 0x0B, 0x0B, 0x00, 0x00, 0x05, 0x0F, 0x00, 0x49,
+ 0x13, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __debug_info
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000000DA
+ content: [ 0x7F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x56, 0x60, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x6A, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x91,
+ 0x78, 0x69, 0x00, 0x00, 0x00, 0x01, 0x01, 0x6A,
+ 0x00, 0x00, 0x00, 0x03, 0x02, 0x91, 0x70, 0x6E,
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x71, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x65, 0x00, 0x00, 0x00, 0x05,
+ 0x04, 0x05, 0x76, 0x00, 0x00, 0x00, 0x05, 0x7B,
+ 0x00, 0x00, 0x00, 0x04, 0x73, 0x00, 0x00, 0x00,
+ 0x06, 0x01, 0x00 ]
+ relocations:
+ - offset: 0x00000037
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000002F
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000026
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000001E
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __DWARF
+ section: __debug_ranges
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015D
+ - segment: __DWARF
+ section: __debug_macinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015D
+ content: [ 0x00 ]
+ - segment: __DWARF
+ section: __apple_names
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015E
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6A, 0x7F, 0x9A, 0x7C,
+ 0x2C, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __apple_objc
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000019A
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __DWARF
+ section: __apple_namespac
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000001BE
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __DWARF
+ section: __apple_types
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000001E2
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x03, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x30, 0x80, 0x88, 0x0B, 0x63, 0x20, 0x95, 0x7C,
+ 0x40, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x6A, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __apple_exttypes
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x0000000000000248
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ alignment: 8
+ address: 0x0000000000000270
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000290
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x50, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __debug_line
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000002D0
+ content: [ 0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1B, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0xFB, 0x0E, 0x0D, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x68, 0x77, 0x2E, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x05, 0x03, 0x0A, 0x08, 0x3D, 0x02, 0x02,
+ 0x00, 0x01, 0x01 ]
+ relocations:
+ - offset: 0x00000028
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
--- /dev/null
+# REQUIRES: system-linker-mach-o
+#
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml 2> %t.err
+# RUN: FileCheck %s < %t.err
+#
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml -demangle 2> %t.err2
+# RUN: FileCheck %s --check-prefix=DCHECK < %t.err2
+#
+# Test -demangle option works on undefined symbol errors.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
+ 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x0000000B
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000006
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000001
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: __Z1xv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: __Znam
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: __Znotcpp
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: __Znotcpp
+# CHECK: __Znam
+# CHECK: _foo
+
+# DCHECK: __Znotcpp
+# DCHECK: operator new[](unsigned long)
+# DCHECK: _foo
+
--- /dev/null
+# Test -dependency_info option
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -test_file_usage \
+# RUN: -dependency_info %t.info \
+# RUN: -path_exists /System/Library/Frameworks \
+# RUN: -path_exists /System/Library/Frameworks/Foo.framework/Foo \
+# RUN: -path_exists /Custom/Frameworks \
+# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \
+# RUN: -F/Custom/Frameworks \
+# RUN: -framework Bar \
+# RUN: -framework Foo
+# RUN: %python %p/Inputs/DependencyDump.py %t.info | FileCheck %s
+
+
+# CHECK: linker-vers: lld
+# CHECK: input-file: /Custom/Frameworks{{[/\\]}}Bar.framework{{[/\\]}}Bar
+# CHECK: not-found: /Custom/Frameworks{{[/\\]}}Foo.framework{{[/\\]}}Foo
+# CHECK: input-file: /System/Library/Frameworks{{[/\\]}}Foo.framework{{[/\\]}}Foo
+# CHECK: output-file: a.out
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
+# RUN: llvm-objdump -r -s --section="__eh_frame" --macho %t | FileCheck --check-prefix=CODE %s
+# RUN: llvm-objdump -r -s --section="__eh_frame" --macho %t2 | FileCheck --check-prefix=CODE %s
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x80, 0x52,
+ 0xFD, 0x7B, 0xC1, 0xA8, 0xC0, 0x03, 0x5F, 0xD6 ]
+ relocations:
+ - offset: 0x00000010
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 9
+ - offset: 0x0000000C
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000008
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000020
+ content: [ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F,
+ 0x72, 0x6C, 0x64, 0x00 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000030
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000050
+ content: [ 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x1E, 0x07, 0x00, 0x9D, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x10, 0x0C, 0x1F, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x48, 0x0E, 0x10, 0x9E, 0x01, 0x9D, 0x02 ]
+ - segment: __TEXT
+ section: __gcc_except_tab
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x00000000000000A0
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: L_str
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000020
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000020
+ - name: ltmp2
+ type: N_SECT
+ sect: 3
+ value: 0x0000000000000030
+ - name: ltmp3
+ type: N_SECT
+ sect: 4
+ value: 0x0000000000000050
+ - name: ltmp4
+ type: N_SECT
+ sect: 4
+ value: 0x0000000000000070
+global-symbols:
+ - name: __Z3fooi
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: __gxx_personality_v0
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _puts
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: L{{[0-9]*}}
+# CHECK: scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 48, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 00 ]
+# CHECK: merge: by-content
+# CHECK: - ref-name: L{{[0-9]*}}
+# CHECK: type: unwind-cfi
+# CHECK: content: [ 1C, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 50, 4C,
+# CHECK: 52, 00, 01, 78, 1E, 07, 00, {{..}}, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, 00, 10, 0C, 1F, 00 ]
+# CHECK: - type: unwind-cfi
+# CHECK: content: [ 24, 00, 00, 00, 24, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 20, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 48, 0E, 10,
+# CHECK: 9E, 01, 9D, 02 ]
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: L{{[0-9]*}}
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 8
+# CHECK: target: __Z3fooi
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 25
+# CHECK: target: L{{[0-9]*}}
+# CHECK: - ref-name: L{{[0-9]*}}
+# CHECK: type: unwind-lsda
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: - type: compact-unwind
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 20, 00, 00, 00,
+# CHECK: 00, 00, 00, 03, 00, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: alignment: 8
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: __Z3fooi
+# CHECK: - name: __Z3fooi
+# CHECK: scope: global
+# CHECK: content: [ FD, 7B, BF, A9, FD, 03, 00, 91, 00, 00, 00, 90,
+# CHECK: 00, 00, 00, 91, 00, 00, 00, 94, 00, 00, 80, 52,
+# CHECK: FD, 7B, C1, A8, C0, 03, 5F, D6 ]
+# CHECK: alignment: 4
+# CHECK: references:
+# CHECK: - kind: page21
+# CHECK: offset: 8
+# CHECK: target: L{{[0-9]*}}
+# CHECK: - kind: offset12
+# CHECK: offset: 12
+# CHECK: target: L{{[0-9]*}}
+# CHECK: - kind: branch26
+# CHECK: offset: 16
+# CHECK: target: _puts
+
+# Make sure we don't have any relocations in the __eh_frame section
+# CODE-NOT: RELOCATION RECORDS FOR [__eh_frame]
+
+# Also make sure the reloc for the FDE->function is the correct offset
+# It should be the offset from the fixup location back to the address
+# of the function we are referencing
+# CODE: Contents of section __TEXT,__eh_frame:
+# This is the CIE:
+# CODE-NEXT: {{[0-9abcdef]*}} 1c000000 00000000 017a504c 52000178
+# CODE-NEXT: {{[0-9abcdef]*}} 1e0700bd ffffffff ffffff00 100c1f00
+# This is the FDE:
+# CODE-NEXT: {{[0-9abcdef]*}} 24000000 24000000 a8ffffff ffffffff
+# This is the important offset for FDE->func ^~~~~~~~ ~~~~~~~~
+
+# CODE-NEXT: {{[0-9abcdef]*}} 20000000 00000000 08c3ffff ffffffff
+# And this is the offset for FDE->lsda ^~~~~~~~ ~~~~~~
+# CODE-NEXT: {{[0-9abcdef]*}} ff480e10 9e019d02
+# And this byte ^~
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -o %t1
+# RUN: llvm-nm -m -n %t1 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -dead_strip -o %t2
+# RUN: llvm-nm -m -n %t2 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -dylib -o %t3
+# RUN: llvm-nm -m -n %t3 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -bundle -o %t4
+# RUN: llvm-nm -m -n %t4 | FileCheck %s
+#
+# Test that ___dso_handle symbol is available for executables, bundles, and dylibs
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000008
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _d
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000008
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: ___dso_handle
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK_NOT: ___dso_handle
+# CHECK: _main
--- /dev/null
+# Check we accept -install_name correctly:
+# RUN: ld64.lld.darwinold -arch x86_64 -install_name libwibble.dylib -dylib \
+# RUN: -compatibility_version 2.0 -current_version 5.3 \
+# RUN: %p/Inputs/x86_64/libSystem.yaml %s -o %t.dylib
+# RUN: llvm-objdump --private-headers %t.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
+
+# Check we read LC_ID_DYLIB correctly:
+# RUN: ld64.lld.darwinold -arch x86_64 %p/Inputs/use-dylib-install-names.yaml \
+# RUN: %p/Inputs/x86_64/libSystem.yaml %t.dylib -dylib -o %t2.dylib
+# RUN: llvm-objdump --private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-READ
+
+# Check we default the install-name to the output file:
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -o libwibble.dylib \
+# RUN: -compatibility_version 2.0 -current_version 5.3 \
+# RUN: %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --private-headers libwibble.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
+# RUN: rm -f libwibble.dylib
+
+# Check -single_module does nothing
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -install_name libwibble.dylib \
+# RUN: -compatibility_version 2.0 -current_version 5.3 \
+# RUN: -single_module -o %t2.dylib %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90,
+ 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3,
+ 0x31, 0xC0, 0xC3 ]
+local-symbols:
+ - name: _myStatic
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000B
+global-symbols:
+ - name: _myGlobal
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+...
+
+
+# CHECK-BINARY-WRITE: cmd LC_ID_DYLIB
+# CHECK-BINARY-WRITE-NEXT: cmdsize 40
+# CHECK-BINARY-WRITE-NEXT: name libwibble.dylib (offset 24)
+# CHECK-BINARY-WRITE-NEXT: time stamp 1
+# CHECK-BINARY-WRITE-NEXT: current version 5.3.0
+# CHECK-BINARY-WRITE-NEXT: compatibility version 2.0.0
+
+# CHECK-BINARY-READ: cmd LC_LOAD_DYLIB
+# CHECK-BINARY-READ-NEXT: cmdsize 56
+# CHECK-BINARY-READ-NEXT: name /usr/lib/libSystem.B.dylib (offset 24)
+# CHECK-BINARY-READ-NEXT: time stamp 2
+# CHECK-BINARY-READ-NEXT: current version 1.0.0
+# CHECK-BINARY-READ-NEXT: compatibility version 1.0.0
+
+# CHECK-BINARY-READ: cmd LC_LOAD_DYLIB
+# CHECK-BINARY-READ-NEXT: cmdsize 40
+# CHECK-BINARY-READ-NEXT: name libwibble.dylib (offset 24)
+# CHECK-BINARY-READ-NEXT: time stamp 2
+# CHECK-BINARY-READ-NEXT: current version 5.3.0
+# CHECK-BINARY-READ-NEXT: compatibility version 2.0.0
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
+# RUN: llvm-objdump -r -s --section="__eh_frame" --macho %t | FileCheck --check-prefix=CODE %s
+# RUN: llvm-objdump -r -s --section="__eh_frame" --macho %t2 | FileCheck --check-prefix=CODE %s
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0x03, 0x5F, 0xD6, 0xC0, 0x03, 0x5F, 0xD6,
+ 0xC0, 0x03, 0x5F, 0xD6 ]
+ - segment: __TEXT
+ section: __gcc_except_tab
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000014
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x000000000000001C
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000020
+ content: [ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000020
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000060
+ content: [ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x1E, 0x07, 0x9B, 0xED, 0xFF, 0xFF, 0xFF, 0x10,
+ 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0xDC, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0xCB, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
+ 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x1E, 0x07, 0x9B, 0xA9, 0xFF, 0xFF, 0xFF, 0x10,
+ 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x83, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
+ 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x0000007D
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 6
+ - offset: 0x0000007D
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 3
+ - offset: 0x0000006C
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 6
+ - offset: 0x0000006C
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 8
+ - offset: 0x0000005B
+ type: ARM64_RELOC_POINTER_TO_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 10
+ - offset: 0x00000035
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 6
+ - offset: 0x00000035
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000024
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 6
+ - offset: 0x00000024
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 7
+ - offset: 0x00000013
+ type: ARM64_RELOC_POINTER_TO_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 9
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000014
+ - name: _bar1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000014
+ - name: _bar2
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000018
+ - name: ltmp12
+ type: N_SECT
+ sect: 3
+ value: 0x000000000000001C
+ - name: ltmp13
+ type: N_SECT
+ sect: 4
+ value: 0x0000000000000020
+ - name: ltmp16
+ type: N_SECT
+ sect: 5
+ value: 0x0000000000000060
+global-symbols:
+ - name: __Z3fooi
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000008
+ - name: __Z4foo2i
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: __gxx_personality_v0
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: __gxx_personality_v1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000004
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000010
+ - name: _someData
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x000000000000001C
+page-size: 0x00000000
+...
+
+# CHECK: --- !native
+# CHECK: path: '<linker-internal>'
+# CHECK: defined-atoms:
+# CHECK: - ref-name: L000
+# CHECK: type: unwind-cfi
+# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
+# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
+# CHECK: 10, 0C, 1F, 00 ]
+# CHECK: alignment: 8
+# CHECK: references:
+# CHECK: - kind: unwindCIEToPersonalityFunction
+# CHECK: offset: 19
+# CHECK: target: __gxx_personality_v0
+# CHECK: - type: unwind-cfi
+# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
+# CHECK: 01, 9D, 02, 00, 00, 00, 00, 00 ]
+# CHECK: alignment: 4 mod 8
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: L000
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 8
+# CHECK: target: __Z3fooi
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 25
+# CHECK: target: _bar1
+# CHECK: - ref-name: L001
+# CHECK: type: unwind-cfi
+# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
+# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
+# CHECK: 10, 0C, 1F, 00 ]
+# CHECK: alignment: 8
+# CHECK: references:
+# CHECK: - kind: unwindCIEToPersonalityFunction
+# CHECK: offset: 19
+# CHECK: target: __gxx_personality_v1
+# CHECK: - type: unwind-cfi
+# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
+# CHECK: 01, 9D, 02, 00, 00, 00, 00, 00 ]
+# CHECK: alignment: 4 mod 8
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: L001
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 8
+# CHECK: target: __Z4foo2i
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 25
+# CHECK: target: _bar2
+# CHECK: - name: _bar1
+# CHECK: type: unwind-lsda
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: - name: _bar2
+# CHECK: type: unwind-lsda
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: - name: _someData
+# CHECK: scope: global
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: - name: __gxx_personality_v0
+# CHECK: scope: global
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: alignment: 4
+# CHECK: - name: __gxx_personality_v1
+# CHECK: scope: global
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: alignment: 4
+# CHECK: - name: __Z3fooi
+# CHECK: scope: global
+# CHECK: content: [ C0, 03, 5F, D6 ]
+# CHECK: alignment: 4
+# CHECK: - name: __Z4foo2i
+# CHECK: scope: global
+# CHECK: content: [ C0, 03, 5F, D6 ]
+# CHECK: alignment: 4
+# CHECK: - name: _main
+# CHECK: scope: global
+# CHECK: content: [ C0, 03, 5F, D6 ]
+# CHECK: alignment: 4
+# CHECK: ...
+
+# # Make sure we don't have any relocations in the __eh_frame section
+# CODE-NOT: RELOCATION RECORDS FOR [__eh_frame]
+
+# Also make sure the reloc for the CIE->personality function is the
+# correct offset
+# It should be the offset from the fixup location back to the address
+# of the function we are referencing
+# CODE: Contents of section __TEXT,__eh_frame:
+# This is the CIE:
+# CODE-NEXT: {{[0-9abcdef]*}} 18000000 00000000 037a504c 52000178
+# CODE-NEXT: {{[0-9abcdef]*}} 1e079bd1 ffffff10 100c1f00 28000000
+# This is the important offset for CIE->pfunc
+# ^~~~~~~~~
+# Then we have an FDE starting from 28000000 above
+# CODE-NEXT: {{[0-9abcdef]*}} 20000000 c8ffffff ffffffff 04000000
+# CODE-NEXT: {{[0-9abcdef]*}} 00000000 08c3ffff ffffffff ff0e109e
+# And a new CIE starts at this 00000018 right below here
+# CODE-NEXT: {{[0-9abcdef]*}} 019d0200 00000000 18000000 00000000
+# CODE-NEXT: {{[0-9abcdef]*}} 037a504c 52000178 1e079b8d ffffff10
+# This is the important offset for its CIE->pfunc ^~~~~~~~~
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t
+#
+# Test that writing empty mach-o sections does not segfault the linker.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+...
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -macosx_version_min 10.8 %s %p/Inputs/hello-world-x86.yaml -o %t && llvm-nm -m %t | FileCheck %s
+# RUN: not ld64.lld.darwinold -arch i386 -ios_simulator_version_min 5.0 %s %p/Inputs/hello-world-x86.yaml -o %t 2>&1 | FileCheck %s --check-prefix=ERROR
+#
+# Test that i386 can link with a macos version but gives an error with a simulator version.
+#
+
+--- !mach-o
+arch: x86
+OS: Mac OS X
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x90 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
+
+# ERROR: cannot be linked due to incompatible operating systems
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -e start %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-readobj --sections %t | FileCheck %s
+
+# Make sure data gets put at offset
+
+--- !native
+defined-atoms:
+ - name: start
+ scope: global
+ content: [ 90 ]
+
+ - name: _s1
+ type: data
+ content: [ 31, 32, 33, 34 ]
+
+ - name: _s2
+ type: zero-fill
+ size: 8192
+
+ - name: _s3
+ type: zero-fill
+ size: 100
+
+ - name: _s4
+ type: data
+ content: [ 01 ]
+
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __text
+# CHECK: Segment: __TEXT
+# CHECK: Size: 0x1
+# CHECK: Offset: 0
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __data
+# CHECK: Segment: __DATA
+# CHECK: Size: 0x5
+# CHECK: Offset: 4096
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __bss
+# CHECK: Segment: __DATA
+# CHECK: Size: 0x2064
+# CHECK: Offset: 0
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-readobj --sections --section-data %t | FileCheck %s
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ 90 ]
+
+ - name: _s2
+ type: data
+ content: [ 31, 32, 33, 34 ]
+
+ - name: _kustom
+ scope: global
+ type: unknown
+ content: [ 01, 02, 03, 04, 05, 06, 07, 08 ]
+ section-choice: custom-required
+ section-name: __CUST/__custom
+
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __text
+# CHECK: Segment: __TEXT
+# CHECK: Size: 0x1
+# CHECK: Offset: 4095
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __data
+# CHECK: Segment: __DATA
+# CHECK: Size: 0x4
+# CHECK: Offset: 4096
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 31323334
+# CHECK-NEXT: )
+
+# CHECK-LABEL: Section {
+# CHECK: Name: __custom{{ }}
+# CHECK: Segment: __CUST{{ }}
+# CHECK: Size: 0x8
+# CHECK: Offset: 8192
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 01020304 05060708
+# CHECK-NEXT: )
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t && \
+# RUN: llvm-objdump --macho --exports-trie %t | FileCheck %s
+#
+#
+# Tests that exports trie builds properly.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3, 0xC3, 0xC3, 0xC3 ]
+global-symbols:
+ - name: _myHidden
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _myRegular
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myWeak
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000002
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000003
+...
+
+# CHECK-NOT: _myHidden
+# CHECK: 0x100000FFD _myRegular
+# CHECK: 0x100000FFE _myWeak [weak_def]
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 %s %p/Inputs/hello-world-x86.yaml -o %t
+# RUN: llvm-objdump --macho --exports-trie %t | FileCheck %s
+#
+# Test that the export trie is emitted in order.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00,
+ 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF,
+ 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000E
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000021
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000B
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000021
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: Exports trie:
+# CHECK-NEXT: __mh_execute_header
+# CHECK-NEXT: _main
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -dylib \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t \
+# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \
+# RUN: llvm-nm -m %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -dylib \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t2 \
+# RUN: -exported_symbol _foo -exported_symbol _b && \
+# RUN: llvm-nm -m %t2 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -dylib \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t3 \
+# RUN: -unexported_symbol _bar -unexported_symbol _a && \
+# RUN: llvm-nm -m %t3 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -dylib \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -dead_strip -o %t \
+# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \
+# RUN: llvm-nm -m %t | FileCheck -check-prefix=CHECK_DEAD %s
+#
+# Test -exported_symbols_list and -exported_symbol properly changes visibility.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48,
+ 0x89, 0xE5, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x000000000000000C
+ content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ]
+
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x000000000000000C
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000010
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000006
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK: (__DATA,__data) non-external (was a private external) _a
+# CHECK: (__DATA,__data) external _b
+# CHECK: (__TEXT,__text) non-external (was a private external) _bar
+# CHECK: (__TEXT,__text) external _foo
+
+# CHECK_DEAD-NOT: (__DATA,__data) non-external (was a private external) _a
+# CHECK_DEAD: (__DATA,__data) external _b
+# CHECK_DEAD-NOT: (__TEXT,__text) non-external (was a private external) _bar
+# CHECK_DEAD: (__TEXT,__text) external _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t -exported_symbol _bar \
+# RUN: && llvm-nm -m %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t2 -keep_private_externs \
+# RUN: -exported_symbol _bar && \
+# RUN: llvm-nm -m %t2 | FileCheck -check-prefix=CHECK_KPE %s
+#
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s -o %t3 \
+# RUN: -exported_symbol _foo 2> %t4
+
+# Test -exported_symbols_list properly changes visibility in -r mode.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48,
+ 0x89, 0xE5, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x000000000000000C
+ content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ]
+
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x000000000000000C
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 2
+ value: 0x0000000000000010
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000006
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK: (__DATA,__data) non-external (was a private external) _a
+# CHECK: (__DATA,__data) non-external (was a private external) _b
+# CHECK: (__TEXT,__text) external _bar
+# CHECK: (__TEXT,__text) non-external (was a private external) _foo
+
+# CHECK_KPE: (__DATA,__data) non-external (was a private external) _a
+# CHECK_KPE: (__DATA,__data) private external _b
+# CHECK_KPE: (__TEXT,__text) external _bar
+# CHECK_KPE: (__TEXT,__text) private external _foo
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -dylib \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t -exported_symbol _foobar 2> %t2
+#
+# Test -exported_symbol fails if exported symbol not found.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48,
+ 0x89, 0xE5, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x000000000000000C
+ content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ]
+
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x000000000000000C
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000010
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000006
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK: (__DATA,__data) private external _a
+# CHECK: (__DATA,__data) external _b
+# CHECK: (__TEXT,__text) private external _bar
+# CHECK: (__TEXT,__text) external _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t \
+# RUN: -L %p/Inputs -lfoo %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that fat archives are handled.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10,
+ 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0,
+ 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000012
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -test_file_usage \
+# RUN: -filelist %p/Inputs/full.filelist \
+# RUN: -path_exists /foo/bar/a.o \
+# RUN: -path_exists /foo/bar/b.o \
+# RUN: -path_exists /foo/x.a \
+# RUN: 2>&1 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -test_file_usage -t \
+# RUN: -filelist %p/Inputs/partial.filelist,/foo \
+# RUN: -path_exists /foo/bar/a.o \
+# RUN: -path_exists /foo/bar/b.o \
+# RUN: -path_exists /foo/x.a \
+# RUN: 2>&1 | FileCheck %s
+
+
+# CHECK: Found filelist entry /foo/bar/a.o
+# CHECK: Found filelist entry /foo/bar/b.o
+# CHECK: Found filelist entry /foo/x.a
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined error %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | FileCheck %s
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ E9, 00, 00, 00, 00 ]
+ alignment: 16
+ references:
+ - kind: branch32
+ offset: 1
+ target: _bar
+undefined-atoms:
+ - name: _bar
+
+# Make sure we error out for -flat_namespace -undefined error.
+# CHECK: Undefined symbol: : _bar
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined suppress %s -o %t %p/Inputs/x86_64/libSystem.yaml
+#
+# Sanity check '-flat_namespace -undefined suppress'.
+# This should pass without error, even though '_bar' is undefined.
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ E9, 00, 00, 00, 00 ]
+ alignment: 16
+ references:
+ - kind: branch32
+ offset: 1
+ target: _bar
+undefined-atoms:
+ - name: _bar
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %p/Inputs/bar.yaml \
+# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/x86_64/libSystem.yaml -o %t1.dylib
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -all_load %t1.dylib \
+# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/x86_64/libSystem.yaml -o %t
+# RUN: llvm-nm -m %t | FileCheck %s
+#
+#
+# Test -all_load does not break linking with dylibs
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9,
+ 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: (__TEXT,__text) external _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
+# RUN: %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t1
+# RUN: llvm-nm -m -n %t1 | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
+# RUN: -force_load %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t2
+# RUN: llvm-nm -m -n %t2 | FileCheck --check-prefix=CHECKF %s
+#
+# Test that -force_load causes members of static library to be loaded.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK-NOT: {{[0-9a-f]+}} (__TEXT,__text) external _main
+
+# CHECKF: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECKF: {{[0-9a-f]+}} (__TEXT,__text) external _foo
+# CHECKF-NOT: {{[0-9a-f]+}} (__TEXT,__text) external _bar
--- /dev/null
+#
+# Test framework and SDK search paths.
+# myFrameworks is not an absolute path, so it should not by found in SDK
+# /Custom/Frameworks should be found in SDK
+# /opt/Frameworks should not be found in SDK
+# /System/Library/Frameworks is implicit and should be in SDK
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -r -test_file_usage -v \
+# RUN: -path_exists myFrameworks \
+# RUN: -path_exists myFrameworks/my.framework/my \
+# RUN: -path_exists /opt/Frameworks \
+# RUN: -path_exists /opt/Frameworks/other.framework/other \
+# RUN: -path_exists /Custom/Frameworks \
+# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \
+# RUN: -path_exists /System/Library/Frameworks \
+# RUN: -path_exists /System/Library/Frameworks/Foo.framework/Foo \
+# RUN: -path_exists /SDK/myFrameworks \
+# RUN: -path_exists /SDK/myFrameworks/my.framework/my \
+# RUN: -path_exists /SDK/Custom/Frameworks \
+# RUN: -path_exists /SDK/Custom/Frameworks/Bar.framework/Bar \
+# RUN: -path_exists /SDK/System/Library/Frameworks \
+# RUN: -path_exists /SDK/System/Library/Frameworks/Foo.framework/Foo \
+# RUN: -syslibroot /SDK \
+# RUN: -FmyFrameworks \
+# RUN: -F/Custom/Frameworks \
+# RUN: -F/opt/Frameworks \
+# RUN: -framework my \
+# RUN: -framework Bar \
+# RUN: -framework Foo \
+# RUN: -framework other \
+# RUN: 2>&1 | FileCheck %s
+
+# CHECK: Framework search paths:
+# CHECK-NEXT: myFrameworks
+# CHECK-NEXT: /SDK/Custom/Frameworks
+# CHECK-NEXT: /opt/Frameworks
+# CHECK-NEXT: /SDK/System/Library/Frameworks
+# CHECK: Found framework myFrameworks/my.framework/my
+# CHECK: Found framework /SDK/Custom/Frameworks/Bar.framework/Bar
+# CHECK: Found framework /SDK/System/Library/Frameworks/Foo.framework/Foo
+# CHECK: Found framework /opt/Frameworks/other.framework/other
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -function_starts && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_function_starts && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -function_starts -no_function_starts && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: Load command {{[0-9]*}}
+# CHECK: cmd LC_FUNCTION_STARTS
+# CHECK: cmdsize 16
+# CHECK: dataoff
+# CHECK: datasize
+
+# NO_FUNCTION_STARTS-NOT: LC_FUNCTION_STARTS
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 %s \
+# RUN: -dylib %p/Inputs/arm64/libSystem.yaml -o %t
+# RUN: llvm-objdump --section-headers %t | FileCheck %s
+
+# Make sure that the GOT relocation from gcc_except_tab to the data
+# is not removed.
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ FD, 7B, BF, A9, FD, 03, 00, 91, FF, 43, 00, D1,
+ BF, C3, 1F, B8, 00, 00, 00, 94, BF, 03, 00, 91,
+ FD, 7B, C1, A8, C0, 03, 5F, D6 ]
+ alignment: 4
+ - name: __ZTSP1A
+ scope: hidden
+ type: constant
+ content: [ 50, 31, 41, 00 ]
+ merge: as-weak
+ - name: GCC_except_table0
+ type: unwind-lsda
+ content: [ FF, 9B, E7, 80, 00, 03, 5B, 00, 00, 00, 00, 1C,
+ 00, 00, 00, 00, 00, 00, 00, 00, 1C, 00, 00, 00,
+ 18, 00, 00, 00, 84, 00, 00, 00, 03, 40, 00, 00,
+ 00, 10, 00, 00, 00, 94, 00, 00, 00, 03, 60, 00,
+ 00, 00, 20, 00, 00, 00, B4, 00, 00, 00, 05, 80,
+ 00, 00, 00, 68, 00, 00, 00, 00, 00, 00, 00, 00,
+ E8, 00, 00, 00, 08, 00, 00, 00, 28, 01, 00, 00,
+ 00, F0, 00, 00, 00, 74, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 01, 7D, 01, 00, A8, FF, FF, FF ]
+ alignment: 4
+ references:
+ - kind: delta32ToGOT
+ offset: 104
+ target: __ZTIP1A
+ - name: __ZTIP1A
+ scope: hidden
+ type: data
+ content: [ 10, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 80, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00 ]
+ merge: as-weak
+ alignment: 16
+shared-library-atoms:
+ - name: dyld_stub_binder
+ load-name: /usr/lib/libSystem.B.dylib
+ type: unknown
+...
+
+# Make sure we have a GOT relocation.
+# This could only have come from __gcc_except_tab to __ZTIP1A
+# CHECK: __got
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/got-order.yaml \
+# RUN: %p/Inputs/got-order2.yaml -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --macho --bind %t | FileCheck %s
+#
+# Test that GOT slots are sorted by name
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x0D, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x8B, 0x00, 0x03, 0x01, 0x48, 0x8B,
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000019
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x0000000E
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000007
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+global-symbols:
+ - name: _func
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _aaa
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _fff
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _zzz
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _aaa
+# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _bar
+# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _fff
+# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _foo
+# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _zazzle
+# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _zzz
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t
+# RUN: llvm-nm -m -n %t | FileCheck %s
+# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK-PRIVATE-HEADER
+#
+# Test that arm64 hello-world can be linked into a mach-o executable
+#
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x08, 0x00, 0x00, 0x90, 0x08, 0x01, 0x40, 0xF9,
+ 0x00, 0x01, 0x40, 0xF9, 0x01, 0x00, 0x00, 0x90,
+ 0x21, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x94,
+ 0x00, 0x00, 0x80, 0x52, 0xFD, 0x7B, 0xC1, 0xA8,
+ 0xC0, 0x03, 0x5F, 0xD6 ]
+ relocations:
+ - offset: 0x0000001C
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000018
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000014
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000C
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x000000000000002C
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: l_.str
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: ___stdoutp
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _fprintf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: (undefined) external ___stdoutp (from libSystem)
+# CHECK: (undefined) external _fprintf (from libSystem)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+
+# CHECK-PRIVATE-HEADER: sectname __stubs
+# CHECK-PRIVATE-HEADER-NEXT: segname __TEXT
+# CHECK-PRIVATE-HEADER-NEXT: addr
+# CHECK-PRIVATE-HEADER-NEXT: size
+# CHECK-PRIVATE-HEADER-NEXT: offset
+# CHECK-PRIVATE-HEADER-NEXT: align 2^1 (2)
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv6 %s %p/Inputs/hello-world-armv6.yaml -o %t
+# RUN: llvm-nm -m %t | FileCheck %s
+#
+# Test that armv6 (arm) hello-world can be linked into a mach-o executable
+#
+
+--- !mach-o
+arch: armv6
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x80, 0x40, 0x2D, 0xE9, 0x10, 0x00, 0x9F, 0xE5,
+ 0x0D, 0x70, 0xA0, 0xE1, 0x00, 0x00, 0x8F, 0xE0,
+ 0xFA, 0xFF, 0xFF, 0xEB, 0x00, 0x00, 0xA0, 0xE3,
+ 0x80, 0x80, 0xBD, 0xE8, 0x0C, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x0000001C
+ scattered: true
+ type: ARM_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000C
+ - offset: 0x00000010
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000020
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: (undefined) external _printf (from libSystem)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 %s %p/Inputs/hello-world-armv7.yaml -o %t
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that armv7 (thumb) hello-world can be linked into a mach-o executable
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x80, 0xB5, 0x40, 0xF2, 0x06, 0x00, 0x6F, 0x46,
+ 0xC0, 0xF2, 0x00, 0x00, 0x78, 0x44, 0xFF, 0xF7,
+ 0xF8, 0xEF, 0x00, 0x20, 0x80, 0xBD ]
+ relocations:
+ - offset: 0x0000000E
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000008
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x00000016
+ - offset: 0x00000006
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x0000000C
+ - offset: 0x00000002
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000016
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000C
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000016
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: (undefined) external _printf (from libSystem)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external [Thumb] _main
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 %s %p/Inputs/hello-world-x86.yaml -o %t
+# RUN: llvm-nm -m %t | FileCheck %s
+#
+# Test that i386 hello-world can be linked into a mach-o executable
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00,
+ 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF,
+ 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000E
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000021
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000B
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000021
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: (undefined) external _printf (from libSystem)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/hello-world-x86_64.yaml \
+# RUN: -o %t
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/hello-world-x86_64.yaml \
+# RUN: -dead_strip -o %t2
+# RUN: llvm-nm -m -n %t2 | FileCheck %s
+#
+# Test that x86_64 hello-world can be linked into a mach-o executable
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x8B, 0x38, 0x48, 0x8D,
+ 0x35, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8,
+ 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000018
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000011
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 0
+ - offset: 0x00000007
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000020
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000028
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000048
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x98, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: L1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000020
+ - name: EH_frame0
+ type: N_SECT
+ sect: 4
+ value: 0x0000000000000048
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _main.eh
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 4
+ value: 0x0000000000000060
+undefined-symbols:
+ - name: ___stdoutp
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _fprintf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: (undefined) external ___stdoutp (from libSystem)
+# CHECK: (undefined) external _fprintf (from libSystem)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) [referenced dynamically] external __mh_execute_header
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 %s -o %t -image_base 31415926000 %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-readobj --macho-segment %t | FileCheck %s
+# RUN: not ld64.lld.darwinold -arch x86_64 -image_base 0x31415926530 %s >/dev/null 2> %t
+# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-MISPAGED
+# RUN: not ld64.lld.darwinold -arch x86_64 -image_base 1000 %s >/dev/null 2> %t
+# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-OVERLAP
+# RUN: not ld64.lld.darwinold -arch x86_64 -image_base hithere %s >/dev/null 2> %t
+# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-NOTHEX
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: []
+
+# CHECK: Segment {
+# CHECK: Cmd: LC_SEGMENT_64
+# CHECK: Name: __TEXT
+# CHECK-NEXT: Size: 152
+# CHECK-NEXT: vmaddr: 0x31415926000
+# CHECK-NEXT: vmsize: 0x1000
+
+
+# CHECK-ERROR-MISPAGED: error: image_base must be a multiple of page size (0x1000)
+
+# CHECK-ERROR-OVERLAP: error: image_base overlaps with __PAGEZERO
+
+# CHECK-ERROR-NOTHEX: error: image_base expects a hex number
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -macosx_version_min 10.8 %s -r -o %t \
+# RUN: && ld64.lld.darwinold -r %t -o %t2 -print_atoms | FileCheck %s
+#
+# Test linker can detect architecture without -arch option.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: defined-atoms:
+# CHECK: - name: _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/interposing-section.yaml \
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --private-headers %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 %s -r -o %t1
+# RUN: llvm-objdump --private-headers %t1 | FileCheck %s
+#
+# Test that interposing section is preserved by linker.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9,
+ 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - segment: __DATA
+ section: __interpose
+ type: S_INTERPOSING
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000010
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+local-symbols:
+ - name: _my_open
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: __interpose_open
+ type: N_SECT
+ sect: 2
+ desc: [ N_NO_DEAD_STRIP ]
+ value: 0x0000000000000010
+undefined-symbols:
+ - name: _open
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: sectname __interposing
+# CHECK: segname __DATA
+# CHECK: type S_INTERPOSING
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t \
+# RUN: && llvm-nm -m %t | FileCheck %s
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t2 -keep_private_externs \
+# RUN: && llvm-nm -m %t2 | FileCheck -check-prefix=CHECK_KPE %s
+#
+# Test -keep_private_externs in -r mode.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48,
+ 0x89, 0xE5, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x000000000000000C
+ content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ]
+
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x000000000000000C
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 2
+ value: 0x0000000000000010
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000006
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK: (__DATA,__data) external _a
+# CHECK: (__DATA,__data) non-external (was a private external) _b
+# CHECK: (__TEXT,__text) external _bar
+# CHECK: (__TEXT,__text) non-external (was a private external) _foo
+
+# CHECK_KPE: (__DATA,__data) external _a
+# CHECK_KPE: (__DATA,__data) private external _b
+# CHECK_KPE: (__TEXT,__text) external _bar
+# CHECK_KPE: (__TEXT,__text) private external _foo
--- /dev/null
+# REQUIRES: x86
+
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/lazy-bind-x86_64.yaml %p/Inputs/lazy-bind-x86_64-2.yaml \
+# RUN: %p/Inputs/lazy-bind-x86_64-3.yaml -o %t \
+# RUN: %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --macho --lazy-bind %t | FileCheck %s
+# RUN: llvm-nm -m %t | FileCheck --check-prefix=CHECK-NM %s
+# RUN: llvm-objdump --disassemble %t | FileCheck --check-prefix=CHECK-HELPERS %s
+# RUN: llvm-objdump --private-headers %t | FileCheck --check-prefix=CHECK-DYLIBS %s
+#
+# Test that correct two-level namespace ordinals are used for lazy bindings.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0x31, 0xC0, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000015
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x0000000E
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000007
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _baz
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: libbar _bar
+# CHECK: libbaz _baz
+# CHECK: libfoo _foo
+
+
+# CHECK-NM: (undefined) external _bar (from libbar)
+# CHECK-NM: (undefined) external _baz (from libbaz)
+# CHECK-NM: (undefined) external _foo (from libfoo)
+
+
+# CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper:
+# CHECK-HELPERS: 68 00 00 00 00 pushq $0
+# CHECK-HELPERS: 68 0b 00 00 00 pushq $11
+# CHECK-HELPERS: 68 16 00 00 00 pushq $22
+
+# Make sure the stub helper is correctly aligned
+# CHECK-DYLIBS: sectname __stub_helper
+# CHECK-DYLIBS-NEXT: segname __TEXT
+# CHECK-DYLIBS-NEXT: addr
+# CHECK-DYLIBS-NEXT: size
+# CHECK-DYLIBS-NEXT: offset
+# CHECK-DYLIBS-NEXT: align 2^2 (4)
+
+# Make sure the __nl_symbol_ptr section is used instea of __got as this is x86_64
+# CHECK-DYLIBS: sectname __nl_symbol_ptr
+# CHECK-DYLIBS-NEXT: segname __DATA
+
+# CHECK-DYLIBS: cmd LC_LOAD_DYLIB
+# CHECK-DYLIBS: name /usr/lib/libbar.dylib (offset 24)
+# CHECK-DYLIBS: current version 2.3.0
+# CHECK-DYLIBS: compatibility version 1.0.0
+# CHECK-DYLIBS: cmd LC_LOAD_DYLIB
+# CHECK-DYLIBS: name /usr/lib/libfoo.dylib (offset 24)
+# CHECK-DYLIBS: current version 3.4.0
+# CHECK-DYLIBS: compatibility version 2.0.0
+# CHECK-DYLIBS: cmd LC_LOAD_DYLIB
+# CHECK-DYLIBS: name /usr/lib/libbaz.dylib (offset 24)
+# CHECK-DYLIBS: current version 4.5.0
+# CHECK-DYLIBS: compatibility version 3.0.0
+
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -o %t %s && llvm-objdump --private-headers %t | FileCheck %s
+
+# CHECK: filesize 19
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00 ]
+ - segment: __TEXT
+ section: __alt
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000010
+ content: [ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -syslibroot %p/Inputs/lib-search-paths -lmyshared -lmystatic -lfile.o -r -print_atoms 2>&1 | FileCheck %s
+
+--- !native
+undefined-atoms:
+ - name: _from_myshared
+ - name: _from_mystatic
+ - name: _from_fileo
+
+# CHECK: defined-atoms:
+# CHECK: - name: _from_fileo
+# CHECK: content: [ 2A, 00, 00, 00 ]
+# CHECK: - name: _from_mystatic
+# CHECK: content: [ 02, 00, 00, 00 ]
+# CHECK: shared-library-atoms:
+# CHECK: - name: _from_myshared
+# CHECK: load-name: libmyshared.dylib
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %p/Inputs/libfoo.a %s -o %t \
+# RUN: %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that if library is before object file on command line, it still is used.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10,
+ 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0,
+ 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000012
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %p/Inputs/libfoo.a %p/Inputs/libbar.a \
+# RUN: %s -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that static libraries are automatically rescanned (bar needs foo).
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10,
+ 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0,
+ 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000012
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _bar
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo
--- /dev/null
+# RUN: not ld64.lld.darwinold -test_file_usage -v \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /Applications/MySDK/usr/local/lib \
+# RUN: -path_exists /Applications/MySDK/usr/lib \
+# RUN: -path_exists /Applications/MySDK/usr/lib/libSystem.dylib \
+# RUN: -syslibroot /Applications/MySDK \
+# RUN: -syslibroot / \
+# RUN: -lSystem \
+# RUN: 2>&1 | FileCheck %s
+
+# When the last -syslibroot is simply "/", all of them get discarded. So in this
+# case, only /usr/lib should show up.
+
+# CHECK: Library search paths:
+# CHECK: /usr/lib
+# CHECK-NOT: /usr/local/lib
+# CHECK: Unable to find library for -lSystem
--- /dev/null
+# RUN: ld64.lld.darwinold -test_file_usage -v \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /Applications/MyFirstSDK/usr/local/lib \
+# RUN: -path_exists /Applications/MySecondSDK/usr/local/lib \
+# RUN: -path_exists /Applications/MyFirstSDK/usr/local/lib/libSystem.a \
+# RUN: -path_exists /Applications/MySecondSDK/usr/local/lib/libSystem.a \
+# RUN: -syslibroot /Applications/MyFirstSDK \
+# RUN: -syslibroot /Applications/MySecondSDK \
+# RUN: -lSystem \
+# RUN: 2>&1 | FileCheck %s
+
+
+# CHECK: Library search paths:
+# CHECK: /usr/lib
+# CHECK: /Applications/MyFirstSDK/usr/local/lib
+# CHECK: /Applications/MySecondSDK/usr/local/lib
+# CHECK: Found library /Applications/MyFirstSDK/usr/local/lib/libSystem.a
--- /dev/null
+# RUN: ld64.lld.darwinold -test_file_usage -v \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /Applications/MySDK/usr/local/lib \
+# RUN: -path_exists /Applications/MySDK/usr/local/lib/libSystem.a \
+# RUN: -path_exists /hasFoo \
+# RUN: -path_exists /hasFoo/foo.o \
+# RUN: -syslibroot /Applications/MySDK \
+# RUN: -L/hasFoo \
+# RUN: -lSystem -lfoo.o \
+# RUN: 2>&1 | FileCheck %s
+
+# When just one -syslibroot is specified, we apparently want to skip *system*
+# paths that aren't found. User ones should still get added. In this case
+# /usr/lib exists, but not the equivalent in the -syslibroot, so there should be
+# no mention of /usr/lib.
+
+# CHECK: Library search paths:
+# CHECK: /hasFoo
+# CHECK-NOT: /usr/lib
+# CHECK-NOT: /usr/local/lib
+# CHECK: /Applications/MySDK/usr/local/lib
+# CHECK-NOT: /usr/lib
+# CHECK-NOT: /usr/local/lib
+# CHECK: Found library /Applications/MySDK/usr/local/lib/libSystem.a
+# CHECK: Found library /hasFoo/foo.o
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -test_file_usage -v \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /usr/local/lib \
+# RUN: -path_exists /usr/lib/libSystem.dylib \
+# RUN: -path_exists hasFoo \
+# RUN: -path_exists hasFoo/libFoo.dylib \
+# RUN: -path_exists /hasBar \
+# RUN: -path_exists /hasBar/libBar.dylib \
+# RUN: -L hasFoo \
+# RUN: -L /hasBar \
+# RUN: -lSystem -lFoo -lBar \
+# RUN: 2>&1 | FileCheck %s
+
+# CHECK: Library search paths:
+# CHECK: hasFoo
+# CHECK: /hasBar
+# CHECK: /usr/lib
+# CHECK: /usr/local/lib
+# CHECK: Found library /usr/lib/libSystem.dylib
+# CHECK: Found library hasFoo/libFoo.dylib
+# CHECK: Found library /hasBar/libBar.dylib
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -test_file_usage -v \
+# RUN: -path_exists hasFoo \
+# RUN: -path_exists hasFoo/libFoo.dylib \
+# RUN: -path_exists /hasBar \
+# RUN: -path_exists /hasBar/libBar.dylib \
+# RUN: -path_exists /SDK/hasFoo \
+# RUN: -path_exists /SDK/hasFoo/libFoo.dylib \
+# RUN: -path_exists /SDK/hasBar \
+# RUN: -path_exists /SDK/hasBar/libBar.dylib \
+# RUN: -syslibroot /SDK \
+# RUN: -L hasFoo \
+# RUN: -L /hasBar \
+# RUN: -lFoo -lBar \
+# RUN: 2>&1 | FileCheck %s
+
+# CHECK: Library search paths:
+# CHECK: hasFoo
+# CHECK: /SDK/hasBar
+# CHECK: Found library hasFoo/libFoo.dylib
+# CHECK: Found library /SDK/hasBar/libBar.dylib
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -test_file_usage -v \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /usr/local/lib \
+# RUN: -path_exists /usr/lib/libSystem.dylib \
+# RUN: -path_exists hasFoo \
+# RUN: -path_exists hasFoo/libFoo.dylib \
+# RUN: -path_exists /hasBar \
+# RUN: -path_exists /hasBar/libBar.dylib \
+# RUN: -L hasFoo \
+# RUN: -L /hasBar \
+# RUN: -Z \
+# RUN: -lFoo -lBar \
+# RUN: 2>&1 | FileCheck %s
+
+# CHECK: Library search paths:
+# CHECK: hasFoo
+# CHECK: /hasBar
+# CHECK-NOT: /usr/lib
+# CHECK-NOT: /usr/local/lib
+# CHECK: Found library hasFoo/libFoo.dylib
+# CHECK: Found library /hasBar/libBar.dylib
--- /dev/null
+
+# mach-o test cases encode input files in yaml and use .yaml extension
+config.suffixes = ['.yaml']
+config.excludes = ['Inputs']
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -dylib \
+# RUN: -macosx_version_min 10.10 -sdk_version 10.10 \
+# RUN: -install_name /usr/lib/foo.dylib \
+# RUN: %p/Inputs/x86_64/libSystem.yaml && \
+# RUN: llvm-readobj %t
+
+# (Tests that lld doesn't crash or produce an invalid file.)
+
+--- !native
+path: '<linker-internal>'
+defined-atoms:
+ - name: _foo
+ scope: global
+ type: unknown
+ content: [ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00 ]
+ alignment: 16
+ section-choice: custom-required
+ section-name: '__TEXT/__foo'
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.4 %s %p/Inputs/hello-world-x86_64.yaml -o %t && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_LIB64
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.5 %s %p/Inputs/hello-world-x86_64.yaml -o %t && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=LIB64
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib -macosx_version_min 10.5 %s %p/Inputs/hello-world-x86_64.yaml -o %t && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=DYLIB
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: start
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+
+...
+
+# NO_LIB64: MH_MAGIC_64 X86_64 ALL 0x00 EXECUTE
+# LIB64: MH_MAGIC_64 X86_64 ALL LIB64 EXECUTE
+# DYLIB: MH_MAGIC_64 X86_64 ALL 0x00 DYLIB
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -bundle -o %t %p/Inputs/x86_64/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 %s -bundle -dead_strip -o %t %p/Inputs/x86_64/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
+#
+# Test that __mh_bundle_header symbol is available for bundles
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000008
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _d
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000008
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_NO_DEAD_STRIP ]
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: __mh_bundle_header
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK: __mh_bundle_header
+# CHECK: _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -dylib -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test that __mh_dylib_header symbol is available for dylibs
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000008
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _d
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000008
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: __mh_dylib_header
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+
+...
+
+# CHECK_NOT: __mh_dylib_header
+# CHECK: _foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s
+
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_catlist
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ alignment: 8
+ address: 0x00000000000003F8
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+undefined-symbols:
+ - name: __category1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: __category2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
+
+# Make sure we atomize the category list section by pointer sized atoms.
+
+# CHECK: path: '<linker-internal>'
+# CHECK: defined-atoms:
+# CHECK: - type: objc-category-list
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: merge: by-content
+# CHECK: alignment: 8
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: __category2
+# CHECK: - type: objc-category-list
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: merge: by-content
+# CHECK: alignment: 8
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: __category1
+# CHECK: undefined-atoms:
+# CHECK: - name: __category1
+# CHECK: - name: __category2
+# CHECK: ...
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s 2>&1 | FileCheck %s
+
+# The file is built for the host, but the objc image info flags are for
+# the simulator.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 ]
+...
+
+# CHECK: {{.*}} cannot be linked. It contains ObjC built for the simulator while we are linking a non-simulator target
\ No newline at end of file
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s 2>&1 | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+...
+
+# CHECK: error: __DATA/__objc_imageinfo in file {{.*}} should be 8 bytes in size
\ No newline at end of file
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s 2>&1 | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 ]
+...
+
+# CHECK: error: __DATA/__objc_imageinfo in file {{.*}} should have version=0
\ No newline at end of file
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s %p/Inputs/swift-version-1.yaml 2>&1 | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00 ]
+...
+
+# CHECK: different swift versions
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -ios_simulator_version_min 5.0 -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s
+
+# Make sure that we have an objc image info in the output. It should have
+# been generated by the objc pass.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00 ]
+...
+
+# CHECK: --- !native
+# CHECK: path: '<linker-internal>'
+# CHECK: defined-atoms:
+# CHECK: - scope: hidden
+# CHECK: type: objc-image-info
+# CHECK: content: [ 00, 00, 00, 00, 20, 02, 00, 00 ]
+# CHECK: alignment: 4
+# CHECK: ...
\ No newline at end of file
--- /dev/null
+# RUN: not ld64.lld.darwinold -ios_simulator_version_min 5.0 -arch x86_64 -r %s 2>&1 | FileCheck %s
+
+# The file is built for the simulator, but the objc image info flags are for
+# the host.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+...
+
+# CHECK: {{.*}} cannot be linked. It contains ObjC built for a non-simulator target while we are linking a simulator target
\ No newline at end of file
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s 2>&1 | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 ]
+...
+
+# CHECK: error: __DATA/__objc_imageinfo in file {{.*}} uses GC. This is not supported
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -o %t \
+# RUN: -exported_symbol .objc_class_name_Foo %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-nm -m %t | FileCheck %s
+#
+# Test that exported objc classes can be specificed using old naming
+# (.e.g .objc_class_name_Foo instead of _OBJC_CLASS_$_Foo)
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __DATA
+ section: __objc_data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000030
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000028
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: '_OBJC_CLASS_$_Foo'
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: '_OBJC_METACLASS_$_Foo'
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000028
+...
+
+# CHECK: (__DATA,__objc_data) external _OBJC_CLASS_$_Foo
+# CHECK: (__DATA,__objc_data) external _OBJC_METACLASS_$_Foo
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
+# RUN: -order_file %p/Inputs/order_file-basic.order \
+# RUN: -force_load %p/Inputs/libfoo.a -o %t
+# RUN: llvm-nm -m -n %t | FileCheck %s
+#
+# Test -order_file
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3, 0xC3, 0xC3, 0xC3 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000014
+ content: [ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _data1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000014
+ - name: _data2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000018
+ - name: _data3
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x000000000000001C
+ - name: _func1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _func2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _func3
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000002
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000003
+...
+
+
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func2
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func1
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func3
+# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data3
+# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data1
+# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data2
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test multiple labels to same address parse into aliases.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC, 0xC3 ]
+local-symbols:
+ - name: _pad
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _myStaticAlias1
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myStaticAlias3
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myStaticAlias2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000001
+global-symbols:
+ - name: _myGlobalFunc1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myGlobalFunc2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myGlobalFunc3
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myHiddenAlias1
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myHiddenAlias2
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myHiddenAlias3
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000001
+...
+
+# CHECK: defined-atoms:
+# CHECK: - name: _pad
+# CHECK: scope: global
+# CHECK: content: [ CC ]
+# CHECK: - name: _myStaticAlias1
+# CHECK: - name: _myStaticAlias2
+# CHECK: - name: _myStaticAlias3
+# CHECK: - name: _myHiddenAlias1
+# CHECK: scope: hidden
+# CHECK: - name: _myHiddenAlias2
+# CHECK: scope: hidden
+# CHECK: - name: _myHiddenAlias3
+# CHECK: scope: hidden
+# CHECK: - name: _myGlobalFunc1
+# CHECK: scope: global
+# CHECK: - name: _myGlobalFunc2
+# CHECK: scope: global
+# CHECK: - name: _myGlobalFunc3
+# CHECK: scope: global
+# CHECK: content: [ C3 ]
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing of armv7 relocations.
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xF0, 0x4E, 0xF8, 0x00, 0xF0, 0x4E, 0xF8,
+ 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF,
+ 0xFF, 0xF7, 0xF6, 0xBF, 0x40, 0xF2, 0x72, 0x01,
+ 0xC0, 0xF2, 0x00, 0x01, 0x40, 0xF2, 0x7A, 0x02,
+ 0xC0, 0xF2, 0x00, 0x02, 0x40, 0xF2, 0x29, 0x01,
+ 0xC0, 0xF2, 0x00, 0x01, 0x79, 0x44, 0x40, 0xF2,
+ 0xA0, 0x03, 0xC0, 0xF2, 0x00, 0x03, 0x40, 0xF2,
+ 0xA8, 0x04, 0xC0, 0xF2, 0x00, 0x04, 0x40, 0xF2,
+ 0x57, 0x03, 0xC0, 0xF2, 0x00, 0x03, 0x40, 0xF2,
+ 0x00, 0x05, 0xC0, 0xF2, 0x00, 0x05, 0x40, 0xF2,
+ 0x08, 0x06, 0xC0, 0xF2, 0x00, 0x06, 0xC0, 0x46,
+ 0x10, 0x00, 0x00, 0xEB, 0x10, 0x00, 0x00, 0xEB,
+ 0xE6, 0xFF, 0xFF, 0xEB, 0xE6, 0xFF, 0xFF, 0xEB,
+ 0xE4, 0xFF, 0xFF, 0xEA, 0x20, 0x10, 0x00, 0xE3,
+ 0x00, 0x10, 0x40, 0xE3, 0x28, 0x20, 0x00, 0xE3,
+ 0x00, 0x20, 0x40, 0xE3, 0x0F, 0x10, 0x81, 0xE0,
+ 0xA0, 0x30, 0x00, 0xE3, 0x00, 0x30, 0x40, 0xE3,
+ 0xA8, 0x40, 0x00, 0xE3, 0x00, 0x40, 0x40, 0xE3,
+ 0x00, 0x50, 0x00, 0xE3, 0x00, 0x50, 0x40, 0xE3,
+ 0x08, 0x60, 0x00, 0xE3, 0x00, 0x60, 0x40, 0xE3 ]
+ relocations:
+ - offset: 0x0000009C
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000098
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000094
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000090
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000008C
+ scattered: true
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x000000A8
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000088
+ scattered: true
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000084
+ type: ARM_RELOC_HALF
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x000000A0
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000080
+ type: ARM_RELOC_HALF
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000078
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 1
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000028
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ value: 0x00000080
+ - offset: 0x00000074
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 0
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ value: 0x00000080
+ - offset: 0x00000070
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 1
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000020
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 1
+ pc-rel: false
+ value: 0x00000080
+ - offset: 0x0000006C
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 0
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 0
+ pc-rel: false
+ value: 0x00000080
+ - offset: 0x00000068
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000064
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000060
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x0000005C
+ scattered: true
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ value: 0x000000A0
+ - offset: 0x00000058
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - offset: 0x00000052
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000004E
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000004A
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000046
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000042
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000057
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000003E
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000003A
+ scattered: true
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x000000A8
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000036
+ scattered: true
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000032
+ type: ARM_RELOC_HALF
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x000000A0
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x0000002E
+ type: ARM_RELOC_HALF
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000000
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16777215
+ - offset: 0x00000028
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x00000056
+ - offset: 0x00000028
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x00000024
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000056
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x00000020
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x0000007A
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x0000001C
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x00000018
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 3
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000072
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 3
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x00000014
+ scattered: true
+ type: ARM_RELOC_HALF_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x000000A0
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000002E
+ - offset: 0x00000010
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x0000000C
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000004
+ scattered: true
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ value: 0x000000A0
+ - offset: 0x00000000
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x00000000000000A0
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0xA4, 0xFF, 0xFF, 0xFF,
+ 0xA4, 0xFF, 0xFF, 0xFF, 0x45, 0xFF, 0xFF, 0xFF,
+ 0x45, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000020
+ scattered: true
+ type: ARM_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x000000C0
+ - offset: 0x0000001C
+ scattered: true
+ type: ARM_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x000000BC
+ - offset: 0x00000018
+ scattered: true
+ type: ARM_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000058
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x000000B8
+ - offset: 0x00000014
+ scattered: true
+ type: ARM_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000058
+ - offset: 0x00000000
+ scattered: true
+ type: ARM_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x000000B4
+ - offset: 0x00000010
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x0000000C
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ scattered: true
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000004
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+local-symbols:
+ - name: _foo_thumb
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+ - name: _x
+ type: N_SECT
+ sect: 2
+ value: 0x00000000000000A0
+ - name: _t1
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000056
+ - name: _foo_arm
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000058
+undefined-symbols:
+ - name: _undef
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - name: _x
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _foo_thumb
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer32
+# CHECK: offset: 8
+# CHECK: target: _foo_thumb
+# CHECK: addend: 4
+# CHECK: - kind: pointer32
+# CHECK: offset: 12
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer32
+# CHECK: offset: 16
+# CHECK: target: _undef
+# CHECK: addend: 4
+# CHECK: - kind: delta32
+# CHECK: offset: 20
+# CHECK: target: _foo_arm
+# CHECK-NOT: addend:
+# CHECK: - kind: delta32
+# CHECK: offset: 24
+# CHECK: target: _foo_arm
+# CHECK: addend: 4
+# CHECK: - kind: delta32
+# CHECK: offset: 28
+# CHECK: target: _foo_thumb
+# CHECK-NOT: addend:
+# CHECK: - kind: delta32
+# CHECK: offset: 32
+# CHECK: target: _foo_thumb
+# CHECK: addend: 4
+# CHECK: - name: _foo_thumb
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 0
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 4
+# CHECK: target: _x
+# CHECK: addend: 4
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 8
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_bl22
+# CHECK: offset: 12
+# CHECK: target: _undef
+# CHECK: addend: 4
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 16
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw_funcRel
+# CHECK: offset: 20
+# CHECK: target: _x
+# CHECK: addend: -46
+# CHECK: - kind: thumb_movt_funcRel
+# CHECK: offset: 24
+# CHECK: target: _x
+# CHECK: addend: -46
+# CHECK: - kind: thumb_movw_funcRel
+# CHECK: offset: 28
+# CHECK: target: _x
+# CHECK: addend: -38
+# CHECK: - kind: thumb_movt_funcRel
+# CHECK: offset: 32
+# CHECK: target: _x
+# CHECK: addend: -38
+# CHECK: - kind: thumb_movw_funcRel
+# CHECK: offset: 36
+# CHECK: target: _t1
+# CHECK: addend: -46
+# CHECK: - kind: thumb_movt_funcRel
+# CHECK: offset: 40
+# CHECK: target: _t1
+# CHECK: addend: -46
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 46
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 50
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 54
+# CHECK: target: _x
+# CHECK: addend: 8
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 58
+# CHECK: target: _x
+# CHECK: addend: 8
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 62
+# CHECK: target: _t1
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 66
+# CHECK: target: _t1
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 70
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 74
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: thumb_movw
+# CHECK: offset: 78
+# CHECK: target: _undef
+# CHECK: addend: 8
+# CHECK: - kind: thumb_movt
+# CHECK: offset: 82
+# CHECK: target: _undef
+# CHECK: addend: 8
+# CHECK: - name: _t1
+# CHECK: content: [ C0, 46 ]
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: - name: _foo_arm
+# CHECK: references:
+# CHECK-NOT: - kind: modeThumbCode
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 0
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 4
+# CHECK: target: _x
+# CHECK: addend: 4
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 8
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_bl24
+# CHECK: offset: 12
+# CHECK: target: _undef
+# CHECK: addend: 4
+# CHECK: - kind: arm_b24
+# CHECK: offset: 16
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movw_funcRel
+# CHECK: offset: 20
+# CHECK: target: _x
+# CHECK: addend: -40
+# CHECK: - kind: arm_movt_funcRel
+# CHECK: offset: 24
+# CHECK: target: _x
+# CHECK: addend: -40
+# CHECK: - kind: arm_movw_funcRel
+# CHECK: offset: 28
+# CHECK: target: _x
+# CHECK: addend: -32
+# CHECK: - kind: arm_movt_funcRel
+# CHECK: offset: 32
+# CHECK: target: _x
+# CHECK: addend: -32
+# CHECK: - kind: arm_movw
+# CHECK: offset: 40
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movt
+# CHECK: offset: 44
+# CHECK: target: _x
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movw
+# CHECK: offset: 48
+# CHECK: target: _x
+# CHECK: addend: 8
+# CHECK: - kind: arm_movt
+# CHECK: offset: 52
+# CHECK: target: _x
+# CHECK: addend: 8
+# CHECK: - kind: arm_movw
+# CHECK: offset: 56
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movt
+# CHECK: offset: 60
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: arm_movw
+# CHECK: offset: 64
+# CHECK: target: _undef
+# CHECK: addend: 8
+# CHECK: - kind: arm_movt
+# CHECK: offset: 68
+# CHECK: target: _undef
+# CHECK: addend: 8
+# CHECK: undefined-atoms:
+# CHECK: - name: _undef
+
+
+
+
+# .align 2
+# .code 16
+# .thumb_func _foo_thumb
+#_foo_thumb:
+# bl _x
+# bl _x+4
+# bl _undef
+# bl _undef+4
+# b _undef
+# movw r1, :lower16:(_x-L1)
+# movt r1, :upper16:(_x-L1)
+# movw r2, :lower16:(_x+8-L1)
+# movt r2, :upper16:(_x+8-L1)
+# movw r1, :lower16:(_t1-L1)
+# movt r1, :upper16:(_t1-L1)
+# add r1, pc
+#L1:
+# movw r3, :lower16:_x
+# movt r3, :upper16:_x
+# movw r4, :lower16:_x+8
+# movt r4, :upper16:_x+8
+# movw r3, :lower16:_t1
+# movt r3, :upper16:_t1
+# movw r5, :lower16:_undef
+# movt r5, :upper16:_undef
+# movw r6, :lower16:_undef+8
+# movt r6, :upper16:_undef+8
+#
+# .thumb_func _t1
+#_t1:
+# nop
+#
+#
+# .code 32
+# .align 2
+#_foo_arm:
+# bl _x
+# bl _x+4
+# bl _undef
+# bl _undef+4
+# b _undef
+# movw r1, :lower16:(_x-L2)
+# movt r1, :upper16:(_x-L2)
+# movw r2, :lower16:(_x+8-L2)
+# movt r2, :upper16:(_x+8-L2)
+# add r1, pc
+#L2:
+# movw r3, :lower16:_x
+# movt r3, :upper16:_x
+# movw r4, :lower16:_x+8
+# movt r4, :upper16:_x+8
+# movw r5, :lower16:_undef
+# movt r5, :upper16:_undef
+# movw r6, :lower16:_undef+8
+# movt r6, :upper16:_undef+8
+#
+#
+# .data
+#_x: .long 0
+# .long _foo_thumb
+# .long _foo_thumb+4
+# .long _undef
+# .long _undef+4
+# .long _foo_arm - .
+# .long _foo_arm+4- .
+# .long _foo_thumb - .
+# .long _foo_thumb+4 - .
+#
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of mach-o functions.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000000
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x00 ]
+ - segment: __DATA
+ section: __cfstring
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000010
+ content: [ 0x00, 0x00, 0x00, 0x00, 0xC8, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC8, 0x07, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000018
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000010
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000008
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 0
+undefined-symbols:
+ - name: ___CFConstantStringClassReference
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: [[STR1:L[L0-9]+]]
+# CHECK: scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ]
+# CHECK: merge: by-content
+# CHECK: - ref-name: [[STR2:L[L0-9]+]]
+# CHECK: scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 74, 68, 65, 72, 65, 00 ]
+# CHECK: merge: by-content
+# CHECK: - scope: hidden
+# CHECK: type: cfstring
+# CHECK: merge: by-content
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: ___CFConstantStringClassReference
+# CHECK: - kind: pointer32
+# CHECK: offset: 8
+# CHECK: target: [[STR1]]
+# CHECK: - scope: hidden
+# CHECK: type: cfstring
+# CHECK: merge: by-content
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: ___CFConstantStringClassReference
+# CHECK: - kind: pointer32
+# CHECK: offset: 8
+# CHECK: target: [[STR2]]
+# CHECK:undefined-atoms:
+# CHECK: - name: ___CFConstantStringClassReference
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of CFString constants.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000000
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x00 ]
+ - segment: __DATA
+ section: __cfstring
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 4
+ address: 0x0000000000000010
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000030
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000020
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000010
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+local-symbols:
+ - name: Lstr1
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: Lstr2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000006
+undefined-symbols:
+ - name: ___CFConstantStringClassReference
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK:defined-atoms:
+# CHECK: - ref-name: L000
+# CHECK: scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ]
+# CHECK: merge: by-content
+# CHECK: - ref-name: L001
+# CHECK: scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 74, 68, 65, 72, 65, 00 ]
+# CHECK: merge: by-content
+# CHECK: - scope: hidden
+# CHECK: type: cfstring
+# CHECK: merge: by-content
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: ___CFConstantStringClassReference
+# CHECK: - kind: pointer64
+# CHECK: offset: 16
+# CHECK: target: L000
+# CHECK: - scope: hidden
+# CHECK: type: cfstring
+# CHECK: merge: by-content
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: ___CFConstantStringClassReference
+# CHECK: - kind: pointer64
+# CHECK: offset: 16
+# CHECK: target: L001
+# CHECK:undefined-atoms:
+# CHECK: - name: ___CFConstantStringClassReference
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of __LD/__compact_unwind (compact unwind) section.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0xB8, 0x0A, 0x00, 0x00, 0x00,
+ 0x5D, 0xC3, 0x55, 0x89, 0xE5, 0xB8, 0x0A, 0x00,
+ 0x00, 0x00, 0x5D, 0xC3 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x000000000000001C
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000014
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000A
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - type: compact-unwind
+# CHECK: content: [ 00, 00, 00, 00, 0A, 00, 00, 00, 00, 00, 00, 01,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: - type: compact-unwind
+# CHECK: content: [ 10, 00, 00, 00, 0A, 00, 00, 00, 00, 00, 00, 01,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: - name: __Z3foov
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ]
+# CHECK: - name: __Z3barv
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ]
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of __LD/__compact_unwind (compact unwind) section.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xB8, 0x0A, 0x00, 0x00,
+ 0x00, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5, 0xB8,
+ 0x0A, 0x00, 0x00, 0x00, 0x5D, 0xC3 ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000020
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000020
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000B
+...
+
+# CHECK: defined-atoms:
+# CHECK: - type: compact-unwind
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 0B, 00, 00, 00,
+# CHECK: 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: - type: compact-unwind
+# CHECK: content: [ 10, 00, 00, 00, 00, 00, 00, 00, 0B, 00, 00, 00,
+# CHECK: 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: - name: __Z3barv
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ]
+# CHECK: - name: __Z3foov
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ]
--- /dev/null
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch armv7 -r -print_atoms %t -o %t2 | FileCheck %s
+# RUN: ld64.lld.darwinold -arch armv7 -dylib %s -o %t3.dylib %p/Inputs/armv7/libSystem.yaml \
+# RUN: && llvm-objdump --macho --private-headers %t3.dylib | FileCheck --check-prefix=CHECK2 %s
+#
+# Test parsing LC_DATA_IN_CODE
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, 0xBF,
+ 0x00, 0xF0, 0x20, 0xE3, 0x0A, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3 ]
+local-symbols:
+ - name: _foo_thumb
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+ - name: _foo_arm
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000018
+dataInCode:
+ - offset: 0x00000004
+ length: 0x0004
+ kind: DICE_KIND_DATA
+ - offset: 0x00000008
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE32
+ - offset: 0x0000000C
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE16
+ - offset: 0x00000010
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE8
+ - offset: 0x0000001C
+ length: 0x0004
+ kind: DICE_KIND_DATA
+ - offset: 0x00000020
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE32
+ - offset: 0x00000024
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE16
+ - offset: 0x00000028
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE8
+...
+
+
+
+# CHECK: defined-atoms:
+# CHECK: - name: _foo_thumb
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: - kind: modeData
+# CHECK: offset: 4
+# CHECK: addend: 1
+# CHECK: - kind: modeData
+# CHECK: offset: 8
+# CHECK: addend: 4
+# CHECK: - kind: modeData
+# CHECK: offset: 12
+# CHECK: addend: 3
+# CHECK: - kind: modeData
+# CHECK: offset: 16
+# CHECK: addend: 2
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 20
+# CHECK: - name: _foo_arm
+# CHECK: references:
+# CHECK: - kind: modeData
+# CHECK: offset: 4
+# CHECK: addend: 1
+# CHECK: - kind: modeData
+# CHECK: offset: 8
+# CHECK: addend: 4
+# CHECK: - kind: modeData
+# CHECK: offset: 12
+# CHECK: addend: 3
+# CHECK: - kind: modeData
+# CHECK: offset: 16
+# CHECK: addend: 2
+# CHECK: - kind: modeArmCode
+# CHECK: offset: 20
+
+
+# CHECK2: cmd LC_DATA_IN_CODE
+# CHECK2: cmdsize 16
+# CHECK2: datasize 64
+
+
+# .code 16
+# .thumb_func _foo_thumb
+#_foo_thumb:
+# nop
+# nop
+#
+# .data_region
+# .long 0
+# .end_data_region
+#
+# .data_region jt32
+# .long 1
+# .end_data_region
+#
+# .data_region jt16
+# .long 2
+# .end_data_region
+#
+# .data_region jt8
+# .long 3
+# .end_data_region
+#
+# nop
+# nop
+#
+#
+#
+# .code 32
+# .align 2
+#_foo_arm:
+# nop
+#
+# .data_region
+# .long 10
+# .end_data_region
+#
+# .data_region jt32
+# .long 11
+# .end_data_region
+#
+# .data_region jt16
+# .long 12
+# .end_data_region
+#
+# .data_region jt8
+# .long 13
+# .end_data_region
+#
+# nop
+#
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch i386 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing LC_DATA_IN_CODE
+#
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x90, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x03, 0x00,
+ 0x00, 0x00 ]
+local-symbols:
+ - name: _func1
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: _func2
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000B
+dataInCode:
+ - offset: 0x00000002
+ length: 0x0008
+ kind: DICE_KIND_JUMP_TABLE32
+ - offset: 0x0000000E
+ length: 0x0004
+ kind: DICE_KIND_JUMP_TABLE32
+...
+
+
+
+# CHECK: defined-atoms:
+# CHECK: - name: _func1
+# CHECK: references:
+# CHECK: - kind: modeData
+# CHECK: offset: 2
+# CHECK: addend: 4
+# CHECK: - kind: modeCode
+# CHECK: offset: 10
+# CHECK: - name: _func2
+# CHECK: references:
+# CHECK: - kind: modeData
+# CHECK: offset: 3
+# CHECK: addend: 4
+# CHECK-NOT: - kind: modeData
+
+
+
+
+#
+#_func1:
+# nop
+# nop
+# .data_region jt32
+# .long 1
+# .long 2
+# .end_data_region
+# nop
+#
+#
+# _func2:
+# nop
+# nop
+# nop
+# .data_region jt32
+# .long 3
+# .end_data_region
+#
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %s -o %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing and writing of arm64 data relocations.
+#
+# The first step tests if the supplied mach-o file is parsed into the correct
+# set of references. The second step verifies relocations can be round-tripped
+# by writing to a new .o file, then parsing that file which should result in
+# the same references.
+#
+#_test:
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xC0, 0x03, 0x5F, 0xD6 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000004
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0xFF, 0xFF, 0xFF, 0xBE, 0xFF, 0xFF, 0xFF,
+ 0xB0, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000050
+ type: ARM64_RELOC_POINTER_TO_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x0000004C
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x0000004C
+ type: ARM64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000048
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000048
+ type: ARM64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000040
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000038
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000030
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000030
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000028
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000028
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000020
+ type: ARM64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000020
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000018
+ type: ARM64_RELOC_POINTER_TO_GOT
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000010
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000008
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+local-symbols:
+ - name: _v1
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000000C
+global-symbols:
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: L000
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: - name: _v1
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 08, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, E0, FF, FF, FF,
+# CHECK: FF, FF, FF, FF, DC, FF, FF, FF, FF, FF, FF, FF,
+# CHECK: {{..}}, {{..}}, 00, 00, 00, 00, 00, 00, 04, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, C0, FF, FF, FF, BE, FF, FF, FF,
+# CHECK: {{B0|B8}}, {{..}}, FF, FF ]
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer64
+# CHECK: offset: 8
+# CHECK: target: _foo
+# CHECK: addend: 8
+# CHECK: - kind: pointer64ToGOT
+# CHECK: offset: 16
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: delta64
+# CHECK: offset: 24
+# CHECK: target: _foo
+# CHECK: addend: 24
+# CHECK: - kind: delta64
+# CHECK: offset: 32
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: delta64
+# CHECK: offset: 40
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: pointer64
+# CHECK: offset: 48
+# CHECK: target: L000
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer64
+# CHECK: offset: 56
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: delta32
+# CHECK: offset: 64
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: delta32
+# CHECK: offset: 68
+# CHECK: target: _foo
+# CHECK: addend: 2
+# CHECK: - kind: delta32ToGOT
+# CHECK: offset: 72
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - name: _bar
+# CHECK: scope: global
+# CHECK: content: [ C0, 03, 5F, D6 ]
+# CHECK: alignment: 4
+# CHECK: undefined-atoms:
+# CHECK: - name: _foo
+
+# .subsections_via_symbols
+# .text
+# .globl_foo
+# .align2
+# _foo:
+# ret
+# .data
+#Lanon:
+# .quad 0
+#_v1:
+# .quad _foo
+# .quad _foo + 8
+# .quad _foo@GOT
+# .quad _foo + 24 - .
+# .quad _foo - .
+# .quad _foo + 4 - .
+# .quad Lanon
+# .quad Lanon + 4
+# .long _foo - .
+# .long _foo +2 - .
+# .long _foo@GOT - .
+
--- /dev/null
+
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch x86_64 %t -r -print_atoms -o %t2 | FileCheck %s
+#
+# Test parsing and writing of x86_64 data relocations.
+#
+# The first step tests if the supplied mach-o file is parsed into the correct
+# set of references. The second step verifies relocations can be round-tripped
+# by writing to a new .o file, then parsing that file which should result in
+# the same references.
+#
+#_foo:
+# ret
+#
+#_bar:
+# ret
+#
+# .section __DATA,__custom
+#L1:
+# .quad 0
+#
+# .data
+#_d:
+# .quad _foo
+# .quad _foo+4
+# .quad _foo - .
+# .quad L1
+# .quad L1 + 2
+# .quad _foo - .
+# .quad _foo + 4 - .
+# .quad L1 - .
+# .long _foo - .
+# .long _foo + 4 - .
+# .long L1 - .
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xC3, 0xC3 ]
+ - segment: __DATA
+ section: __custom
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000002
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x000000000000000A
+ content: [
+# .quad _foo
+# No addend is needed here as we are referencing _foo directly and that is
+# encoded entirely in the X86_64_RELOC_UNSIGNED
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+# .quad _foo+4
+# Addend of 4 is needed here as we are referencing _foo from the
+# X86_64_RELOC_UNSIGNED, then the addend gives us 4 more.
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+# .quad _foo - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -16 because that is the offset from here back
+# to _d.
+ 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+# .quad . - _foo
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -16 because that is the offset from here back
+# to _d.
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+# .quad L1
+# This is a X86_64_RELOC_UNSIGNED without extern set.
+# In this case, we encode the section number for L1 in the relocation, and
+# the addend here is the absolute address of the location in that section
+# we want to reference.
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+# .quad L1 + 2
+# This is a X86_64_RELOC_UNSIGNED without extern set.
+# In this case, we encode the section number for L1 in the relocation, and
+# the addend here is the absolute address of the location in that section
+# we want to reference. We have a 4 because the section is at address 2
+# and we want an offset of 2 from there.
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+# .quad _foo - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -40 because that is the offset from here back
+# to _d.
+ 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+# .quad _foo + 4 - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -52. It would have been -56 because that
+# would take us from the address of this relocation back to _d. But as
+# we also add 4 for the offset, we get -52.
+ 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+# .quad L1 - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned does not have extern set, so the relocation
+# number is the section number for L1.
+# Note the addend here is -62. Of that, -64 would be the offset from
+# this location from _d. The remaining 2 is the absolute address
+# of L1.
+ 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+# .long _foo - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -72 because that is the offset from here back
+# to _d.
+ 0xB8, 0xFF, 0xFF, 0xFF,
+# .long . - _foo
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -76 because that is the offset from here back
+# to _d.
+ 0xB4, 0xFF, 0xFF, 0xFF,
+# .long _foo + 4 - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned references _foo.
+# Note the addend here is -76. It would have been -80 because that
+# would take us from the address of this relocation back to _d. But as
+# we also add 4 for the offset, we get -76.
+ 0xB4, 0xFF, 0xFF, 0xFF,
+# .long L1 - .
+# This is the pair X86_64_RELOC_SUBTRACTOR and X86_64_RELOC_UNSIGNED.
+# The subtractor references _d which is the first nonlocal label in this
+# section. The unsigned does not have extern set, so the relocation
+# number is the section number for L1.
+# Note the addend here is -82. Of that, -84 would be the offset from
+# this location from _d. The remaining 2 is the absolute address
+# of L1.
+ 0xAE, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000054
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000054
+ type: X86_64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000050
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000050
+ type: X86_64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x0000004C
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x0000004C
+ type: X86_64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000048
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000048
+ type: X86_64_RELOC_UNSIGNED
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000040
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000040
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000038
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000038
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000030
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000030
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000028
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000020
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000018
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000018
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000010
+ type: X86_64_RELOC_SUBTRACTOR
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000010
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000008
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+local-symbols:
+ - name: _foo
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: _bar
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000001
+ - name: _d
+ type: N_SECT
+ sect: 3
+ value: 0x000000000000000A
+page-size: 0x00000000
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - name: _d
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 04, 00, 00, 00,
+# CHECK: 00, 00, 00, 00, F0, FF, FF, FF, FF, FF, FF, FF,
+# CHECK: 18, 00, 00, 00, 00, 00, 00, 00, {{..}}, {{..}}, 00, 00,
+# CHECK: 00, 00, 00, 00, {{..}}, {{..}}, 00, 00, 00, 00, 00, 00,
+# CHECK: D0, FF, FF, FF, FF, FF, FF, FF, CC, FF, FF, FF,
+# CHECK: FF, FF, FF, FF, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: B8, FF, FF, FF, B4, FF, FF, FF, B4, FF, FF, FF,
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}} ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: _foo
+# CHECK: - kind: pointer64
+# CHECK: offset: 8
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: delta64
+# CHECK: offset: 16
+# CHECK: target: _foo
+# CHECK: - kind: negDelta64
+# CHECK: offset: 24
+# CHECK: target: _foo
+# CHECK: - kind: pointer64Anon
+# CHECK: offset: 32
+# CHECK: target: L003
+# CHECK: - kind: pointer64Anon
+# CHECK: offset: 40
+# CHECK: target: L003
+# CHECK: addend: 2
+# CHECK: - kind: delta64
+# CHECK: offset: 48
+# CHECK: target: _foo
+# CHECK: - kind: delta64
+# CHECK: offset: 56
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: delta64Anon
+# CHECK: offset: 64
+# CHECK: target: L003
+# CHECK: - kind: delta32
+# CHECK: offset: 72
+# CHECK: target: _foo
+# CHECK: - kind: negDelta32
+# CHECK: offset: 76
+# CHECK: target: _foo
+# CHECK: - kind: delta32
+# CHECK: offset: 80
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: delta32Anon
+# CHECK: offset: 84
+# CHECK: target: L003
+# CHECK: - name: _foo
+# CHECK: content: [ C3 ]
+# CHECK: dead-strip: never
+# CHECK: - name: _bar
+# CHECK: content: [ C3 ]
+# CHECK: dead-strip: never
+# CHECK: - ref-name: L003
+# CHECK: type: unknown
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__DATA/__custom'
+# CHECK: dead-strip: never
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of mach-o data symbols.
+#
+# long a = 0x0807060504030201;
+# int b = 0x14131211;
+# int c = 0x24232221;
+# static int s1;
+# static int s2 = 0x34333231;
+#
+#
+
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000000
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
+ 0x31, 0x32, 0x33, 0x34, 0x41, 0x42, 0x43, 0x44 ]
+ - segment: __CUST
+ section: __custom
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000018
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ]
+ - segment: __DATA
+ section: __bss
+ type: S_ZEROFILL
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000020
+ size: 4
+local-symbols:
+ - name: _s1
+ type: N_SECT
+ sect: 3
+ value: 0x0000000000000020
+ - name: _s2
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000010
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000008
+ - name: _c
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+ - name: _cWeak
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000014
+ - name: _kustom
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000018
+...
+
+# CHECK: defined-atoms:
+
+# CHECK: - name: _a
+# CHECK: scope: global
+# CHECK: type: data
+# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ]
+
+# CHECK: - name: _b
+# CHECK: scope: global
+# CHECK: type: data
+# CHECK: content: [ 11, 12, 13, 14 ]
+
+# CHECK: - name: _c
+# CHECK: scope: global
+# CHECK: type: data
+# CHECK: content: [ 21, 22, 23, 24 ]
+
+# CHECK: - name: _s2
+# CHECK: type: data
+# CHECK: content: [ 31, 32, 33, 34 ]
+
+# CHECK: - name: _cWeak
+# CHECK: scope: global
+# CHECK: type: data
+# CHECK: content: [ 41, 42, 43, 44 ]
+# CHECK: merge: as-weak
+
+# CHECK: - name: _s1
+# CHECK: type: zero-fill
+# CHECK: size: 4
+
+# CHECK: - name: _kustom
+# CHECK: scope: global
+# CHECK: type: unknown
+# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ]
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__CUST/__custom'
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of x86_64 __eh_frame (dwarf unwind) relocations.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0x5D, 0xC3, 0x48, 0x89, 0xC7, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x66, 0x2E,
+ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0x5D, 0xC3, 0x48, 0x89, 0xC7, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00, 0x00, 0x00,
+ 0x00 ]
+ - segment: __TEXT
+ section: __gcc_except_tab
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 4
+ address: 0x000000000000004C
+ content: [ 0xFF, 0x9B, 0xA2, 0x80, 0x80, 0x00, 0x03, 0x1A,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0x9B, 0xA2, 0x80, 0x80, 0x00, 0x03, 0x1A,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000100
+ content: [ 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x10, 0x07, 0x9B, 0x04, 0x00, 0x00, 0x00, 0x10,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0xD8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0xB0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00,
+ 0x98, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0xCB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000013
+ type: X86_64_RELOC_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 8
+local-symbols:
+ - name: GCC_except_table0
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000004C
+ - name: GCC_except_table2
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000074
+global-symbols:
+ - name: _catchMyException1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _catchMyException2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000030
+ - name: _bar
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000020
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_begin_catch
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_end_catch
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___gxx_personality_v0
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
+
+# Check that LSDA fields are fixed up correctly, even when there are multiple
+# CIEs involved.
+#
+# (1) Check that we can relocate an LSDA at all. Requires correct interpretation
+# of augmentation data strings in CIEs and augmentation data fields of FDEs.
+#
+# CHECK: - type: unwind-cfi
+# CHECK-NOT: - type:
+# CHECK: references:
+# CHECK-NEXT: - kind: negDelta32
+# CHECK-NEXT: offset: 4
+# CHECK-NEXT: target: L002
+# CHECK-NEXT: - kind: unwindFDEToFunction
+# CHECK-NEXT: offset: 8
+# CHECK-NEXT: target: _catchMyException1
+# CHECK-NEXT: - kind: unwindFDEToFunction
+# CHECK-NEXT: offset: 25
+# CHECK-NEXT: target: GCC_except_table0
+#
+# (2) Check that we have an intervening FDE with a different CIE.
+# If the test fails here then test (3) probably isn't testing what it
+# should, and this test-case should be updated.
+#
+# CHECK: - type: unwind-cfi
+# CHECK-NOT: - type:
+# CHECK: references:
+# CHECK-NEXT: - kind: negDelta32
+# CHECK-NEXT: offset: 4
+# CHECK-NEXT: target: L001
+# CHECK-NEXT: - kind: unwindFDEToFunction
+# CHECK-NEXT: offset: 8
+# CHECK-NEXT: target: _bar
+#
+# (3) Check that we can relocate the LSDA on a second FDE that references the
+# original CIE from (1). Requires us to match this FDE up with the correct
+# CIE.
+#
+# CHECK-NEXT: - type: unwind-cfi
+# CHECK-NOT: - type:
+# CHECK: references:
+# CHECK-NEXT: - kind: negDelta32
+# CHECK-NEXT: offset: 4
+# CHECK-NEXT: target: L002
+# CHECK-NEXT: - kind: unwindFDEToFunction
+# CHECK-NEXT: offset: 8
+# CHECK-NEXT: target: _catchMyException2
+# CHECK-NEXT: - kind: unwindFDEToFunction
+# CHECK-NEXT: offset: 25
+# CHECK-NEXT: target: GCC_except_table2
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of new __eh_frame (dwarf unwind) section that has no .eh labels
+# and no relocations.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x56, 0x83, 0xEC, 0x14, 0xE8,
+ 0x00, 0x00, 0x00, 0x00, 0x5E, 0xC7, 0x04, 0x24,
+ 0x04, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xFF, 0xFF,
+ 0xFF, 0xC7, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x8B,
+ 0x8E, 0x38, 0x00, 0x00, 0x00, 0x89, 0x4C, 0x24,
+ 0x04, 0x89, 0x04, 0x24, 0xC7, 0x44, 0x24, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC7, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8,
+ 0xBC, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000040
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000035
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000021
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000044
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000C
+ - offset: 0x00000015
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - segment: __IMPORT
+ section: __pointers
+ type: S_NON_LAZY_SYMBOL_POINTERS
+ attributes: [ ]
+ address: 0x0000000000000044
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+ indirect-syms: [ 5 ]
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000048
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x7C, 0x08, 0x01,
+ 0x10, 0x0C, 0x05, 0x04, 0x88, 0x01, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x98, 0xFF, 0xFF, 0xFF, 0x39, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x08, 0x84, 0x02, 0x42, 0x0D,
+ 0x04, 0x44, 0x86, 0x03, 0x18, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0xB5, 0xFF, 0xFF, 0xFF,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0E, 0x08,
+ 0x84, 0x02, 0x42, 0x0D, 0x04, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000039
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: __ZTIi
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_allocate_exception
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_throw
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: [[CIE:L[L0-9]+]]
+# CHECK: type: unwind-cfi
+# CHECK: content:
+# CHECK: - type: unwind-cfi
+# CHECK: content:
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: delta32
+# CHECK: offset: 8
+# CHECK: target: __Z3foov
+# CHECK: - type: unwind-cfi
+# CHECK: content:
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: delta32
+# CHECK: offset: 8
+# CHECK: target: __Z3barv
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of old __eh_frame (dwarf unwind) section that has .eh labels
+# and relocations.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x56, 0x83, 0xEC, 0x14, 0xE8,
+ 0x00, 0x00, 0x00, 0x00, 0x5E, 0xC7, 0x04, 0x24,
+ 0x04, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xFF, 0xFF,
+ 0xFF, 0xC7, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x8B,
+ 0x8E, 0x38, 0x00, 0x00, 0x00, 0x89, 0x4C, 0x24,
+ 0x04, 0x89, 0x04, 0x24, 0xC7, 0x44, 0x24, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC7, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8,
+ 0xBC, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000040
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000035
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 7
+ - offset: 0x00000021
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000044
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000C
+ - offset: 0x00000015
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 6
+ - segment: __IMPORT
+ section: __pointers
+ type: S_NON_LAZY_SYMBOL_POINTERS
+ attributes: [ ]
+ address: 0x0000000000000044
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+ indirect-syms: [ 5 ]
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000048
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x7C, 0x08, 0x01,
+ 0x10, 0x0C, 0x05, 0x04, 0x88, 0x01, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x98, 0xFF, 0xFF, 0xFF, 0x39, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x08, 0x84, 0x02, 0x42, 0x0D,
+ 0x04, 0x44, 0x86, 0x03, 0x18, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0xB5, 0xFF, 0xFF, 0xFF,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0E, 0x08,
+ 0x84, 0x02, 0x42, 0x0D, 0x04, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x0000001C
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000064
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000048
+ - offset: 0x00000020
+ scattered: true
+ type: GENERIC_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000068
+ - offset: 0x00000038
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000080
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000048
+ - offset: 0x0000003C
+ scattered: true
+ type: GENERIC_RELOC_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000039
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000084
+local-symbols:
+ - name: EH_frame0
+ type: N_SECT
+ sect: 3
+ value: 0x0000000000000048
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000039
+ - name: __Z3barv.eh
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x000000000000007C
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: __Z3foov.eh
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x0000000000000060
+undefined-symbols:
+ - name: __ZTIi
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_allocate_exception
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_throw
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: [[CIE:L[L0-9]+]]
+# CHECK: type: unwind-cfi
+# CHECK: content:
+# CHECK: - type: unwind-cfi
+# CHECK: content:
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: delta32
+# CHECK: offset: 8
+# CHECK: target: __Z3foov
+# CHECK: - type: unwind-cfi
+# CHECK: content:
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: delta32
+# CHECK: offset: 8
+# CHECK: target: __Z3barv
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of __eh_frame (dwarf unwind) section.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xB8, 0x09, 0x00, 0x00,
+ 0x00, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5, 0xB8,
+ 0x0A, 0x00, 0x00, 0x00, 0x5D, 0xC3 ]
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000058
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000B
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: [[CIE:L[0-9]+]]
+# CHECK: type: unwind-cfi
+# CHECK: content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00,
+# CHECK: 01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ]
+# CHECK: - type: unwind-cfi
+# CHECK: content: [ 24, 00, 00, 00, 1C, 00, 00, 00, 88, FF, FF, FF,
+# CHECK: FF, FF, FF, FF, 0B, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+# CHECK: 00, 00, 00, 00 ]
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 8
+# CHECK: target: __Z3barv
+# CHECK: - type: unwind-cfi
+# CHECK: content: [ 24, 00, 00, 00, 44, 00, 00, 00, 6B, FF, FF, FF,
+# CHECK: FF, FF, FF, FF, 0B, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+# CHECK: 00, 00, 00, 00 ]
+# CHECK: references:
+# CHECK: - kind: negDelta32
+# CHECK: offset: 4
+# CHECK: target: [[CIE]]
+# CHECK: - kind: unwindFDEToFunction
+# CHECK: offset: 8
+# CHECK: target: __Z3foov
+# CHECK: - name: __Z3barv
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, B8, 09, 00, 00, 00, 5D, C3 ]
+# CHECK: - name: __Z3foov
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ]
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -o %t
+# RUN: ld64.lld.darwinold -arch x86_64 -r %t -print_atoms -o %t2 | FileCheck %s
+#
+# Test parsing of mach-o functions.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90,
+ 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3,
+ 0xCC, 0x31, 0xC0, 0xC3 ]
+local-symbols:
+ - name: _myStatic
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000B
+global-symbols:
+ - name: _myGlobal
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+ - name: _myGlobalWeak
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000002
+ - name: _myHidden
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ value: 0x0000000000000004
+ - name: _myHiddenWeak
+ type: N_SECT
+ scope: [ N_EXT, N_PEXT ]
+ sect: 1
+ desc: [ N_WEAK_DEF ]
+ value: 0x0000000000000007
+ - name: _myStripNot
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_NO_DEAD_STRIP ]
+ value: 0x0000000000000010
+ - name: _myResolver
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_SYMBOL_RESOLVER ]
+ value: 0x0000000000000011
+...
+
+# CHECK-NOT: name:
+# CHECK: content: [ CC ]
+
+# CHECK: name: _myGlobal
+# CHECK: scope: global
+# CHECK: content: [ C3 ]
+
+# CHECK: name: _myGlobalWeak
+# CHECK: scope: global
+# CHECK: content: [ 90, C3 ]
+# CHECK: merge: as-weak
+
+# CHECK: name: _myHidden
+# CHECK: scope: hidden
+# CHECK: content: [ 90, 90, C3 ]
+
+# CHECK: name: _myHiddenWeak
+# CHECK: scope: hidden
+# CHECK: content: [ 90, 90, 90, C3 ]
+# CHECK: merge: as-weak
+
+# CHECK: name: _myStatic
+# CHECK-NOT: scope: global
+# CHECK-NOT: scope: hidden
+# CHECK: content: [ 90, 90, 90, 90, C3 ]
+
+# CHECK: name: _myStripNot
+# CHECK: scope: global
+# CHECK: content: [ CC ]
+# CHECK: dead-strip: never
+
+# CHECK: name: _myResolver
+# CHECK: scope: global
+# CHECK: type: resolver
+# CHECK: content: [ 31, C0, C3 ]
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of literal sections.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x89, 0xE5,
+ 0x5D, 0xC3, 0x55, 0x89, 0xE5, 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __mod_init_func
+ type: S_MOD_INIT_FUNC_POINTERS
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000044
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000004
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __DATA
+ section: __mod_term_func
+ type: S_MOD_TERM_FUNC_POINTERS
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000104
+ content: [ 0x0A, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _init
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _init2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000005
+ - name: _term
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000A
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - type: initializer-pointer
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: - type: initializer-pointer
+# CHECK: content: [ 05, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: - type: terminator-pointer
+# CHECK: content: [ 0A, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: - name: _init
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, 5D, C3 ]
+# CHECK: - name: _init2
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, 5D, C3 ]
+# CHECK: - name: _term
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, 5D, C3 ]
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of literal sections.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48,
+ 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5,
+ 0x5D, 0xC3 ]
+ - segment: __DATA
+ section: __mod_init_func
+ type: S_MOD_INIT_FUNC_POINTERS
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000008
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - segment: __DATA
+ section: __mod_term_func
+ type: S_MOD_TERM_FUNC_POINTERS
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000108
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 2
+global-symbols:
+ - name: _init
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _init2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000006
+ - name: _term
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x000000000000000C
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - type: initializer-pointer
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: _init
+# CHECK: - type: initializer-pointer
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: _init2
+# CHECK: - type: terminator-pointer
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: dead-strip: never
+# CHECK: references:
+# CHECK: - kind: pointer64
+# CHECK: offset: 0
+# CHECK: target: _term
+# CHECK: - name: _init
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ]
+# CHECK: - name: _init2
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ]
+# CHECK: - name: _term
+# CHECK: scope: global
+# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ]
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t 2> %t.err
+# RUN: FileCheck %s < %t.err
+#
+# Test for error if literal section is not correct size multiple.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __literal8
+ type: S_8BYTE_LITERALS
+ attributes: [ ]
+ alignment: 0
+ address: 0x0000000000000120
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D ]
+...
+
+# CHECK: error:
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of literal sections.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000100
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68,
+ 0x65, 0x72, 0x65, 0x00, 0x77, 0x6F, 0x72, 0x6C,
+ 0x00 ]
+ - segment: __TEXT
+ section: __literal4
+ type: S_4BYTE_LITERALS
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000114
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14,
+ 0x28, 0x29, 0x2A, 0x2B ]
+ - segment: __TEXT
+ section: __literal8
+ type: S_8BYTE_LITERALS
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000120
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F ]
+ - segment: __TEXT
+ section: __literal16
+ type: S_16BYTE_LITERALS
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000130
+ content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00 ]
+ - segment: __TEXT
+ section: __ustring
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 1
+ address: 0x0000000000000100
+ content: [ 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00,
+ 0x6F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x68, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x00, 0x00 ]
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ]
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 74, 68, 65, 72, 65, 00 ]
+# CHECK: - scope: hidden
+# CHECK: type: c-string
+# CHECK: content: [ 77, 6F, 72, 6C, 00 ]
+# CHECK: - scope: hidden
+# CHECK: type: utf16-string
+# CHECK: content: [ 68, 00, 65, 00, 6C, 00, 6C, 00, 6F, 00, 00, 00 ]
+# CHECK: - scope: hidden
+# CHECK: type: utf16-string
+# CHECK: content: [ 74, 00, 68, 00, 65, 00, 72, 00, 00, 00 ]
+# CHECK: - scope: hidden
+# CHECK: type: const-4-byte
+# CHECK: content: [ 01, 02, 03, 04 ]
+# CHECK: - scope: hidden
+# CHECK: type: const-4-byte
+# CHECK: content: [ 11, 12, 13, 14 ]
+# CHECK: - scope: hidden
+# CHECK: type: const-4-byte
+# CHECK: content: [ 28, 29, 2A, 2B ]
+# CHECK: - scope: hidden
+# CHECK: type: const-8-byte
+# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ]
+# CHECK: - scope: hidden
+# CHECK: type: const-8-byte
+# CHECK: content: [ 28, 29, 2A, 2B, 2C, 2D, 2E, 2F ]
+# CHECK: - scope: hidden
+# CHECK: type: const-16-byte
+# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C,
+# CHECK: 0D, 0E, 0F, 00 ]
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of non-lazy-pointer sections.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00, 0x00,
+ 0x59, 0x8D, 0x81, 0x14, 0x00, 0x00, 0x00, 0x8D,
+ 0x81, 0x18, 0x00, 0x00, 0x00, 0x5D, 0xC3, 0x55,
+ 0x89, 0xE5, 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x00000011
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000008
+ - offset: 0x0000000B
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x0000001C
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000008
+ - segment: __IMPORT
+ section: __pointers
+ type: S_NON_LAZY_SYMBOL_POINTERS
+ attributes: [ ]
+ address: 0x000000000000001C
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ indirect-syms: [ 2, 2147483648 ]
+local-symbols:
+ - name: _foo
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000017
+global-symbols:
+ - name: _get
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK:defined-atoms:
+# CHECK: - ref-name: [[GOT1:L[L0-9]+]]
+# CHECK: scope: hidden
+# CHECK: type: got
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: merge: by-content
+# CHECK: - ref-name: [[GOT2:L[L0-9]+]]
+# CHECK: scope: hidden
+# CHECK: type: got
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: merge: by-content
+# CHECK: - name: _get
+# CHECK: scope: global
+# CHECK: content: [ 55, 89, E5, E8, 00, 00, 00, 00, 59, 8D, 81, 14,
+# CHECK: 00, 00, 00, 8D, 81, 18, 00, 00, 00, 5D, C3 ]
+# CHECK: references:
+# CHECK: - kind: funcRel32
+# CHECK: offset: 11
+# CHECK: target: [[GOT1]]
+# CHECK: - kind: funcRel32
+# CHECK: offset: 17
+# CHECK: target: [[GOT2]]
+# CHECK: - name: _foo
+# CHECK: content: [ 55, 89, E5, 5D, C3 ]
+
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch i386 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing and writing of x86 relocations.
+#
+# The first step tests if the supplied mach-o file is parsed into the correct
+# set of references. The second step verifies relocations can be round-tripped
+# by writing to a new .o file, then parsing that file which should result in
+# the same references.
+#
+# .text
+#_test:
+# call _undef
+# call _undef+2
+# call _foo
+# call _foo+2
+# callw _undef
+# callw _foo
+# callw _foo+2
+#L1:
+# movl _undef, %eax
+# movl _x, %eax
+# movl _x+4, %eax
+# movl _x-L1(%eax), %eax
+# movl _x+4-L1(%eax), %eax
+#
+#_foo:
+# ret
+#
+# .data
+#_x:
+# .long _undef
+# .long _undef+7
+# .long _foo
+# .long _foo+3
+# .long _test - .
+# .long _test+3 - .
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xE8, 0xFB, 0xFF, 0xFF, 0xFF, 0xE8, 0xF8, 0xFF,
+ 0xFF, 0xFF, 0xE8, 0x2C, 0x00, 0x00, 0x00, 0xE8,
+ 0x29, 0x00, 0x00, 0x00, 0x66, 0xE8, 0xE8, 0xFF,
+ 0x66, 0xE8, 0x1F, 0x00, 0x66, 0xE8, 0x1D, 0x00,
+ 0xA1, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x3C, 0x00,
+ 0x00, 0x00, 0xA1, 0x40, 0x00, 0x00, 0x00, 0x8B,
+ 0x80, 0x1C, 0x00, 0x00, 0x00, 0x8B, 0x80, 0x20,
+ 0x00, 0x00, 0x00, 0xC3 ]
+ relocations:
+ - offset: 0x00000037
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x0000003C
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x00000031
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x0000003C
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000020
+ - offset: 0x0000002B
+ scattered: true
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ value: 0x0000003C
+ - offset: 0x00000026
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000021
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 3
+ - offset: 0x0000001E
+ scattered: true
+ type: GENERIC_RELOC_VANILLA
+ length: 1
+ pc-rel: true
+ value: 0x0000003B
+ - offset: 0x0000001A
+ type: GENERIC_RELOC_VANILLA
+ length: 1
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 1
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000010
+ scattered: true
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ value: 0x0000003B
+ - offset: 0x0000000B
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000006
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000001
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x000000000000003C
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
+ 0xB4, 0xFF, 0xFF, 0xFF, 0xB3, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000014
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x00000050
+ - offset: 0x00000010
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000000
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000004C
+ - offset: 0x0000000C
+ scattered: true
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ value: 0x0000003B
+ - offset: 0x00000008
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000004
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 3
+ - offset: 0x00000000
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 3
+local-symbols:
+ - name: _test
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: _foo
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000003B
+ - name: _x
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000003C
+undefined-symbols:
+ - name: _undef
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - name: _x
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _undef
+# CHECK: addend: 7
+# CHECK: - kind: pointer32
+# CHECK: offset: 8
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: pointer32
+# CHECK: offset: 12
+# CHECK: target: _foo
+# CHECK: addend: 3
+# CHECK: - kind: delta32
+# CHECK: offset: 16
+# CHECK: target: _test
+# CHECK: - kind: delta32
+# CHECK: offset: 20
+# CHECK: target: _test
+# CHECK: addend: 3
+# CHECK: - name: _test
+# CHECK: references:
+# CHECK: - kind: branch32
+# CHECK: offset: 1
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: branch32
+# CHECK: offset: 6
+# CHECK: target: _undef
+# CHECK: addend: 2
+# CHECK: - kind: branch32
+# CHECK: offset: 11
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: branch32
+# CHECK: offset: 16
+# CHECK: target: _foo
+# CHECK: addend: 2
+# CHECK: - kind: branch16
+# CHECK: offset: 22
+# CHECK: target: _undef
+# CHECK-NOT: addend:
+# CHECK: - kind: branch16
+# CHECK: offset: 26
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: branch16
+# CHECK: offset: 30
+# CHECK: target: _foo
+# CHECK: addend: 2
+# CHECK: - kind: abs32
+# CHECK: offset: 33
+# CHECK: target: _undef
+# CHECK: - kind: abs32
+# CHECK: offset: 38
+# CHECK: target: _x
+# CHECK: - kind: abs32
+# CHECK: offset: 43
+# CHECK: target: _x
+# CHECK: addend: 4
+# CHECK: - kind: funcRel32
+# CHECK: offset: 49
+# CHECK: target: _x
+# CHECK: addend: -32
+# CHECK: - kind: funcRel32
+# CHECK: offset: 55
+# CHECK: target: _x
+# CHECK: addend: -28
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r %s -print_atoms -o %t2 | FileCheck %s
+#
+# Test parsing of mach-o functions with no symbols at all.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC ]
+...
+
+# CHECK-NOT: name:
+# CHECK: content: [ CC ]
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s
+#
+# Test parsing of tentative definitions, including size, scope, and alignment.
+#
+#
+# int tent4;
+# long tent8;
+# __attribute__((visibility("hidden"))) int tentHidden;
+# __attribute__((aligned(16))) int tent4_16;
+# __attribute__((aligned(32))) long tent64_32[8];
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __tex
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+undefined-symbols:
+ - name: _tent4
+ type: N_UNDF
+ scope: [ N_EXT ]
+ desc: 0x0200
+ value: 0x0000000000000004
+ - name: _tent4_16
+ type: N_UNDF
+ scope: [ N_EXT ]
+ desc: 0x0400
+ value: 0x0000000000000004
+ - name: _tent64_32
+ type: N_UNDF
+ scope: [ N_EXT ]
+ desc: 0x0500
+ value: 0x0000000000000040
+ - name: _tent8
+ type: N_UNDF
+ scope: [ N_EXT ]
+ desc: 0x0300
+ value: 0x0000000000000008
+ - name: _tentHidden
+ type: N_UNDF
+ scope: [ N_EXT, N_PEXT ]
+ desc: 0x0200
+ value: 0x0000000000000004
+...
+
+
+# CHECK: defined-atoms:
+# CHECK: name: _tent4
+# CHECK: scope: global
+# CHECK: type: zero-fill
+# CHECK: size: 4
+# CHECK: merge: as-tentative
+# CHECK: alignment: 4
+
+# CHECK: name: _tent4_16
+# CHECK: scope: global
+# CHECK: type: zero-fill
+# CHECK: size: 4
+# CHECK: merge: as-tentative
+# CHECK: alignment: 16
+
+# CHECK: name: _tent64_32
+# CHECK: scope: global
+# CHECK: type: zero-fill
+# CHECK: size: 64
+# CHECK: merge: as-tentative
+# CHECK: alignment: 32
+
+# CHECK: name: _tent8
+# CHECK: scope: global
+# CHECK: type: zero-fill
+# CHECK: size: 8
+# CHECK: merge: as-tentative
+# CHECK: alignment: 8
+
+# CHECK: name: _tentHidden
+# CHECK: scope: hidden
+# CHECK: type: zero-fill
+# CHECK: size: 4
+# CHECK: merge: as-tentative
+# CHECK: alignment: 4
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing and writing of arm64 text relocations.
+#
+# The first step tests if the supplied mach-o file is parsed into the correct
+# set of references. The second step verifies relocations can be round-tripped
+# by writing to a new .o file, then parsing that file which should result in
+# the same references.
+#
+#_test:
+
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x94,
+ 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0x39,
+ 0x20, 0x00, 0x40, 0x79, 0x20, 0x00, 0x40, 0xB9,
+ 0x20, 0x00, 0x40, 0xF9, 0x20, 0x00, 0xC0, 0x3D,
+ 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xB9,
+ 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xF9,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x40, 0xF9 ]
+ relocations:
+ - offset: 0x00000034
+ type: ARM64_RELOC_TLVP_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 5
+ - offset: 0x00000030
+ type: ARM64_RELOC_TLVP_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x0000002C
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 6
+ - offset: 0x00000028
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 6
+ - offset: 0x00000024
+ type: ARM64_RELOC_ADDEND
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16
+ - offset: 0x00000024
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000020
+ type: ARM64_RELOC_ADDEND
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 16
+ - offset: 0x00000020
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x0000001C
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000018
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000014
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000010
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x0000000C
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 2
+ - offset: 0x00000008
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - offset: 0x00000004
+ type: ARM64_RELOC_ADDEND
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 8
+ - offset: 0x00000004
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000038
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: _func
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: _v1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000038
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000038
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _tlv
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _v2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - name: _v1
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 00, 00, 00, 00 ]
+# CHECK: - name: _func
+# CHECK: content: [ 00, 00, 00, 94, 00, 00, 00, 94, 01, 00, 00, 90,
+# CHECK: 20, 00, 40, 39, 20, 00, 40, 79, 20, 00, 40, B9,
+# CHECK: 20, 00, 40, F9, 20, 00, C0, 3D, 01, 00, 00, 90,
+# CHECK: 20, 00, 40, B9, 01, 00, 00, 90, 20, 00, 40, F9,
+# CHECK: 00, 00, 00, 90, 00, 00, 40, F9 ]
+# CHECK: references:
+# CHECK: - kind: branch26
+# CHECK: offset: 0
+# CHECK: target: _foo
+# CHECK: - kind: branch26
+# CHECK: offset: 4
+# CHECK: target: _foo
+# CHECK: addend: 8
+# CHECK: - kind: page21
+# CHECK: offset: 8
+# CHECK: target: _v1
+# CHECK: - kind: offset12
+# CHECK: offset: 12
+# CHECK: target: _v1
+# CHECK: - kind: offset12scale2
+# CHECK: offset: 16
+# CHECK: target: _v1
+# CHECK: - kind: offset12scale4
+# CHECK: offset: 20
+# CHECK: target: _v1
+# CHECK: - kind: offset12scale8
+# CHECK: offset: 24
+# CHECK: target: _v1
+# CHECK: - kind: offset12scale16
+# CHECK: offset: 28
+# CHECK: target: _v1
+# CHECK: - kind: page21
+# CHECK: offset: 32
+# CHECK: target: _v1
+# CHECK: addend: 16
+# CHECK: - kind: offset12scale4
+# CHECK: offset: 36
+# CHECK: target: _v1
+# CHECK: addend: 16
+# CHECK: - kind: gotPage21
+# CHECK: offset: 40
+# CHECK: target: _v2
+# CHECK: - kind: gotOffset12
+# CHECK: offset: 44
+# CHECK: target: _v2
+# CHECK: - kind: tlvPage21
+# CHECK: offset: 48
+# CHECK: target: _tlv
+# CHECK: - kind: tlvOffset12
+# CHECK: offset: 52
+# CHECK: target: _tlv
+# CHECK: undefined-atoms:
+# CHECK: - name: _foo
+# CHECK: - name: _tlv
+# CHECK: - name: _v2
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing and writing of x86_64 text relocations.
+#
+# The first step tests if the supplied mach-o file is parsed into the correct
+# set of references. The second step verifies relocations can be round-tripped
+# by writing to a new .o file, then parsing that file which should result in
+# the same references.
+#
+#_test:
+# call _foo
+# call _foo+4
+# movq _foo@GOTPCREL(%rip), %rax
+# pushq _foo@GOTPCREL(%rip)
+# movl _foo(%rip), %eax
+# movl _foo+4(%rip), %eax
+# movb $0x12, _foo(%rip)
+# movw $0x1234, _foo(%rip)
+# movl $0x12345678, _foo(%rip)
+# movl L2(%rip), %eax
+# movb $0x12, L2(%rip)
+# movw $0x1234, L2(%rip)
+# movl $0x12345678, L2(%rip)
+#
+# .data
+#L2: .long 0
+
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x04, 0x00,
+ 0x00, 0x00, 0x48, 0x8B, 0x05, 0x04, 0x00, 0x00,
+ 0x00, 0xFF, 0x35, 0x04, 0x00, 0x00, 0x00, 0x8B,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x05, 0x04,
+ 0x00, 0x00, 0x00, 0xC6, 0x05, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x12, 0x66, 0xC7, 0x05, 0xFE, 0xFF, 0xFF,
+ 0xFF, 0x34, 0x12, 0xC7, 0x05, 0xFC, 0xFF, 0xFF,
+ 0xFF, 0x78, 0x56, 0x34, 0x12, 0x8B, 0x05, 0x1A,
+ 0x00, 0x00, 0x00, 0xc6, 0x05, 0x13, 0x00, 0x00,
+ 0x00, 0x12, 0x66, 0xc7, 0x05, 0x0a, 0x00, 0x00,
+ 0x00, 0x34, 0x12, 0xc7, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x56, 0x34, 0x12 ]
+ relocations:
+ - offset: 0x00000055
+ type: X86_64_RELOC_SIGNED_4
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - offset: 0x0000004d
+ type: X86_64_RELOC_SIGNED_2
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - offset: 0x00000045
+ type: X86_64_RELOC_SIGNED_1
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - offset: 0x0000003F
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 2
+ - offset: 0x00000035
+ type: X86_64_RELOC_SIGNED_4
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000002D
+ type: X86_64_RELOC_SIGNED_2
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000025
+ type: X86_64_RELOC_SIGNED_1
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000001F
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000019
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000013
+ type: X86_64_RELOC_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000D
+ type: X86_64_RELOC_GOT_LOAD
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000006
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000001
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x000000000000005D
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: _test
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _foo
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: defined-atoms:
+# CHECK: - ref-name: [[LABEL:L[0-9]+]]
+# CHECK: type: data
+# CHECK: content: [ 00, 00, 00, 00 ]
+# CHECK: - name: _test
+# CHECK: references:
+# CHECK: - kind: branch32
+# CHECK: offset: 1
+# CHECK: target: _foo
+# CHECK: - kind: branch32
+# CHECK: offset: 6
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: ripRel32GotLoad
+# CHECK: offset: 13
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: ripRel32Got
+# CHECK: offset: 19
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: ripRel32
+# CHECK: offset: 25
+# CHECK: target: _foo
+# CHECK: - kind: ripRel32
+# CHECK: offset: 31
+# CHECK: target: _foo
+# CHECK: addend: 4
+# CHECK: - kind: ripRel32Minus1
+# CHECK: offset: 37
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Minus2
+# CHECK: offset: 45
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Minus4
+# CHECK: offset: 53
+# CHECK: target: _foo
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Anon
+# CHECK: offset: 63
+# CHECK: target: [[LABEL]]
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Minus1Anon
+# CHECK: offset: 69
+# CHECK: target: [[LABEL]]
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Minus2Anon
+# CHECK: offset: 77
+# CHECK: target: [[LABEL]]
+# CHECK-NOT: addend:
+# CHECK: - kind: ripRel32Minus4Anon
+# CHECK: offset: 85
+# CHECK: target: [[LABEL]]
+# CHECK-NOT: addend:
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && ld64.lld.darwinold -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s
+#
+# Test parsing of x86_64 tlv relocations.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000007
+ type: X86_64_RELOC_TLV
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - segment: __DATA
+ section: __thread_data
+ type: S_THREAD_LOCAL_REGULAR
+ attributes: [ ]
+ alignment: 4
+ address: 0x0000000000000014
+ content: [ 0x07, 0x00, 0x00, 0x00 ]
+ - segment: __DATA
+ section: __thread_vars
+ type: S_THREAD_LOCAL_VARIABLES
+ attributes: [ ]
+ address: 0x0000000000000018
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000010
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 3
+local-symbols:
+ - name: '_x$tlv$init'
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000014
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _x
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x0000000000000018
+undefined-symbols:
+ - name: __tlv_bootstrap
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
+
+# CHECK: - name: _x
+# CHECK-NEXT: scope: global
+# CHECK-NEXT: type: tlv-thunk
+# CHECK-NOT: - name:
+# CHECK: references:
+# CHECK-NEXT: - kind: pointer64
+# CHECK-NEXT: offset: 0
+# CHECK-NEXT: target: __tlv_bootstrap
+# CHECK-NEXT: - kind: tlvInitSectionOffset
+# CHECK-NEXT: offset: 16
+# CHECK-NEXT: target: '_x$tlv$init'
+# CHECK: - name: _main
+# CHECK-NOT: - name:
+# CHECK-NEXT: scope: global
+# CHECK: references:
+# CHECK-NEXT: - kind: ripRel32Tlv
+# CHECK-NEXT: offset: 7
+# CHECK-NEXT: target: _x
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: %p/Inputs/re-exported-dylib-ordinal.yaml \
+# RUN: %p/Inputs/re-exported-dylib-ordinal2.yaml \
+# RUN: %p/Inputs/re-exported-dylib-ordinal3.yaml -dylib -o %t \
+# RUN: && llvm-nm -m %t | FileCheck %s
+#
+# Test that when one dylib A re-exports dylib B that using a symbol from B
+# gets recorded as coming from A.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9,
+ 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _test
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: (undefined) external _bar (from libfoo)
+# CHECK: (undefined) external dyld_stub_binder (from libSystem)
--- /dev/null
+# Check we handle -rpath correctly:
+# RUN: ld64.lld.darwinold -arch x86_64 -rpath @loader_path/../Frameworks \
+# RUN: %p/Inputs/x86_64/libSystem.yaml %s -o %t
+# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 4
+ address: 0x0000000000000000
+ content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90,
+ 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3,
+ 0x31, 0xC0, 0xC3 ]
+local-symbols:
+ - name: _myStatic
+ type: N_SECT
+ sect: 1
+ value: 0x000000000000000B
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+...
+
+
+# CHECK-BINARY-WRITE: cmd LC_RPATH
+# CHECK-BINARY-WRITE-NEXT: cmdsize 40
+# CHECK-BINARY-WRITE-NEXT: path @loader_path/../Frameworks (offset 12)
--- /dev/null
+# RUN: ld64.lld.darwinold -macosx_version_min 10.7 -arch x86_64 -print_atoms %s -o %t | FileCheck %s
+# RUN: not ld64.lld.darwinold -macosx_version_min 10.6 -arch x86_64 -o %t %s 2> %t2
+# RUN: FileCheck < %t2 %s --check-prefix=CHECK-ERROR
+# RUN: llvm-objdump --macho --private-headers %t | FileCheck %s --check-prefix=CHECK-LOADCMDS
+#
+# Test parsing of x86_64 tlv relocations.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000007
+ type: X86_64_RELOC_TLV
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - segment: __DATA
+ section: __thread_bss
+ type: S_THREAD_LOCAL_ZEROFILL
+ attributes: [ ]
+ alignment: 4
+ address: 0x0000000000000014
+ size: 4
+ - segment: __DATA
+ section: __thread_vars
+ type: S_THREAD_LOCAL_VARIABLES
+ attributes: [ ]
+ address: 0x0000000000000018
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000010
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 0
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 3
+ - segment: __DATA
+ section: __dummy
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x00000000000000C0
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+local-symbols:
+ - name: '_x$tlv$init'
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000014
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _x
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x0000000000000018
+ - name: '__tlv_bootstrap'
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 4
+ value: 0x00000000000000C0
+ - name: 'dyld_stub_binder'
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 4
+ value: 0x00000000000000C8
+ - name: 'start'
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 4
+ value: 0x00000000000000D0
+page-size: 0x00000000
+...
+
+# CHECK: - name: _x
+# CHECK-NEXT: scope: global
+# CHECK-NEXT: type: tlv-thunk
+# CHECK-NOT: - name:
+# CHECK: references:
+# CHECK-NEXT: - kind: pointer64
+# CHECK-NEXT: offset: 0
+# CHECK-NEXT: target: __tlv_bootstrap
+# CHECK-NEXT: - kind: tlvInitSectionOffset
+# CHECK-NEXT: offset: 16
+# CHECK-NEXT: target: '_x$tlv$init'
+# CHECK: - name: '_x$tlv$init'
+# CHECK-NEXT: type: tlv-zero-fill
+# CHECK: - name: _main
+# CHECK-NOT: - name:
+# CHECK: references:
+# CHECK-NEXT: - kind: ripRel32
+# CHECK-NEXT: offset: 7
+# CHECK-NEXT: target: L[[ID:[0-9]+]]
+# CHECK: - ref-name: L[[ID]]
+# CHECK-NEXT: scope: hidden
+# CHECK-NEXT: type: tlv-initializer-ptr
+# CHECK-NEXT: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK-NEXT: alignment: 8
+# CHECK-NEXT: permissions: rw-
+# CHECK-NEXT: references:
+# CHECK-NEXT: - kind: pointer64
+# CHECK-NEXT: offset: 0
+# CHECK-NEXT: target: _x
+
+# CHECK-ERROR: targeted OS version does not support use of thread local variables in _main for architecture x86_64
+
+# CHECK-LOADCMDS: sectname __thread_bss
+# CHECK-LOADCMDS: segname __DATA
+# CHECK-LOADCMDS: addr 0x{{[0-9A-F]*}}
+# CHECK-LOADCMDS: size 0x0000000000000004
+# CHECK-LOADCMDS: offset 0
+# CHECK-LOADCMDS: align 2^2 (4)
+# CHECK-LOADCMDS: reloff 0
+# CHECK-LOADCMDS: nreloc 0
+# CHECK-LOADCMDS: type S_THREAD_LOCAL_ZEROFILL
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -sdk_version 10.blah %s -o %t 2>&1 | FileCheck %s --check-prefix=ERROR
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# ERROR: malformed sdkVersion value
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -dylib \
+# RUN: -sectalign __DATA __custom 0x800 -sectalign __TEXT __text 0x400 \
+# RUN: %p/Inputs/x86_64/libSystem.yaml -o %t \
+# RUN: && llvm-readobj --sections %t | FileCheck %s
+#
+# Test -sectalign option on __text and a custom section.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x8B, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x5D, 0xC3 ]
+ relocations:
+ - offset: 0x0000000C
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x00000006
+ type: X86_64_RELOC_SIGNED
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 2
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000014
+ content: [ 0x0A, 0x00, 0x00, 0x00 ]
+ - segment: __DATA
+ section: __custom
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000018
+ content: [ 0x0A, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _a
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 2
+ value: 0x0000000000000014
+ - name: _b
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 3
+ value: 0x0000000000000018
+ - name: _get
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Address: 0xC00
+
+# CHECK: Name: __data (5F 5F 64 61 74 61 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Address: 0x1000
+
+# CHECK: Name: __custom (5F 5F 63 75 73 74 6F 6D 00 00 00 00 00 00 00 00)
+# CHECK: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00)
+# CHECK: Address: 0x1800
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -dylib \
+# RUN: %p/Inputs/x86_64/libSystem.yaml -o %t \
+# RUN: && llvm-objdump --private-headers %t | FileCheck %s
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x8B, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x5D, 0xC3 ]
+global-symbols:
+ - name: _get
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: PURE_INSTRUCTIONS SOME_INSTRUCTIONS
+
--- /dev/null
+# RUN: ld64.lld.darwinold -r -arch x86_64 -o %t -sectcreate __DATA __data \
+# RUN: %p/Inputs/hw.raw_bytes -print_atoms | FileCheck %s
+
+# CHECK: --- !native
+# CHECK: path: '<linker-internal>'
+# CHECK: defined-atoms:
+# CHECK: - scope: global
+# CHECK: type: sectcreate
+# CHECK: content: [ 68, 65, 6C, 6C, 6F, 0A ]
+# CHECK: section-choice: custom-required
+# CHECK: section-name: '__DATA/__data'
+# CHECK: dead-strip: never
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t && llvm-objdump --private-headers %t | FileCheck %s
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: start
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+
+...
+
+# CHECK: Load command 0
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 72
+# CHECK: segname __PAGEZERO
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot ---
+# CHECK: initprot ---
+# CHECK: nsects 0
+# CHECK: flags (none)
+# CHECK: Load command 1
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 152
+# CHECK: segname __TEXT
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot r-x
+# CHECK: initprot r-x
+# CHECK: nsects 1
+# CHECK: flags (none)
+# CHECK: Section
+# CHECK: sectname __text
+# CHECK: segname __TEXT
+# CHECK: addr
+# CHECK: size
+# CHECK: offset
+# CHECK: align 2^0 (1)
+# CHECK: reloff 0
+# CHECK: nreloc 0
+# CHECK: type S_REGULAR
+# CHECK: attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
+# CHECK: reserved1 0
+# CHECK: reserved2 0
+# CHECK: Load command 2
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 72
+# CHECK: segname __LINKEDIT
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot r--
+# CHECK: initprot r--
+# CHECK: nsects 0
+# CHECK: flags (none)
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/hello-world-x86_64.yaml -o %t && llvm-objdump --private-headers %t | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: start
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000001
+
+...
+
+# CHECK: Load command 0
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 72
+# CHECK: segname __PAGEZERO
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot ---
+# CHECK: initprot ---
+# CHECK: nsects 0
+# CHECK: flags (none)
+# CHECK: Load command 1
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 152
+# CHECK: segname __TEXT
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot rwx
+# CHECK: initprot r-x
+# CHECK: nsects 1
+# CHECK: flags (none)
+# CHECK: Section
+# CHECK: sectname __text
+# CHECK: segname __TEXT
+# CHECK: addr
+# CHECK: size
+# CHECK: offset
+# CHECK: align 2^0 (1)
+# CHECK: reloff 0
+# CHECK: nreloc 0
+# CHECK: type S_REGULAR
+# CHECK: attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
+# CHECK: reserved1 0
+# CHECK: reserved2 0
+# CHECK: Load command 2
+# CHECK: cmd LC_SEGMENT_64
+# CHECK: cmdsize 72
+# CHECK: segname __LINKEDIT
+# CHECK: vmaddr
+# CHECK: vmsize
+# CHECK: fileoff
+# CHECK: filesize
+# CHECK: maxprot rwx
+# CHECK: initprot r--
+# CHECK: nsects 0
+# CHECK: flags (none)
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -source_version 10.blah %s -o %t 2>&1 | FileCheck %s --check-prefix=ERROR
+# RUN: ld64.lld.darwinold -arch x86_64 -source_version 10.1.2.3.4 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump --private-headers %t | FileCheck %s
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# ERROR: malformed source_version value
+
+# CHECK: Load command {{[0-9]*}}
+# CHECK: cmd LC_SOURCE_VERSION
+# CHECK: cmdsize 16
+# CHECK: version 10.1.2.3.4
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 %s -o %t %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --private-headers %t | FileCheck --check-prefix=CHECK-DEFAULT %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 %s -o %t -stack_size 31415926000 %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --private-headers %t | FileCheck --check-prefix=CHECK-EXPLICIT %s
+# RUN: not ld64.lld.darwinold -arch x86_64 -stack_size 0x31415926530 %s >/dev/null 2> %t
+# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-MISPAGED
+# RUN: not ld64.lld.darwinold -arch x86_64 -stack_size hithere %s >/dev/null 2> %t
+# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-NOTHEX
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: []
+
+# CHECK-DEFAULT: cmd LC_MAIN
+# CHECK-DEFAULT: stacksize 0
+
+# CHECK-EXPLICIT: cmd LC_MAIN
+# CHECK-EXPLICIT: stacksize 3384796143616
+
+# CHECK-ERROR-MISPAGED: error: stack_size must be a multiple of page size (0x1000)
+
+# CHECK-ERROR-NOTHEX: error: stack_size expects a hex number
--- /dev/null
+# RUN: ld64.lld.darwinold -arch i386 %s %p/Inputs/hello-world-x86.yaml -o %t
+# RUN: obj2yaml %t | FileCheck %s
+#
+# Test that the string table contains a ' ' as its first symbol
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00,
+ 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF,
+ 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000E
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000021
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000B
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000021
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: StringTable:
+# CHECK-NEXT: - ' '
+# CHECK-NEXT: - __mh_execute_header
+# CHECK-NEXT: - _main
+# CHECK-NEXT: - _printf
+# CHECK-NEXT: - dyld_stub_binder
+# CHECK-NEXT: - ''
--- /dev/null
+# REQUIRES: x86
+
+# RUN: mkdir -p %t
+#
+# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/test.o
+# RUN: ld64.lld.darwinold -o %t/test -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem %t/test.o
+#
+# RUN: llvm-objdump --bind --no-show-raw-insn -d -r %t/test | FileCheck %s
+
+# CHECK: Disassembly of section __TEXT,__text:
+# CHECK: movq {{.*}} # [[ADDR:[0-9a-f]+]]
+
+# CHECK: Bind table:
+# CHECK: __DATA __got 0x[[ADDR]] pointer 0 libSystem ___nan
+
+.section __TEXT,__text
+.global _main
+
+_main:
+ movq ___nan@GOTPCREL(%rip), %rax
+ ret
--- /dev/null
+# RUN: ld64.lld.darwinold -ios_simulator_version_min 5.0 -arch x86_64 -r %s -o %t
+# RUN: llvm-readobj --file-headers %t | FileCheck %s
+
+# Make sure that we have an objc image info in the output. It should have
+# been generated by the objc pass.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __DATA
+ section: __objc_imageinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_NO_DEAD_STRIP ]
+ address: 0x0000000000000100
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00 ]
+...
+
+# The ObjC pass creates a new image info in a new MachoFile internal to the pass.
+# Make sure that we still have MH_SUBSECTIONS_VIA_SYMBOLS in the output file, even
+# though that file in the ObjCPass didn't get it set from being parsed.
+
+# CHECK: MH_SUBSECTIONS_VIA_SYMBOLS
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined dynamic_lookup %s -o %t %p/Inputs/x86_64/libSystem.yaml
+#
+# Sanity check '-twolevel_namespace -undefined dynamic_lookup'.
+# This should pass without error, even though '_bar' is undefined.
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ E9, 00, 00, 00, 00 ]
+ alignment: 16
+ references:
+ - kind: branch32
+ offset: 1
+ target: _bar
+undefined-atoms:
+ - name: _bar
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined warning %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | \
+# RUN: FileCheck --check-prefix=CHECK-WARNING %s
+# RUN: not ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined suppress %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | \
+# RUN: FileCheck --check-prefix=CHECK-SUPPRESS %s
+
+--- !native
+defined-atoms:
+ - name: _main
+ scope: global
+ content: [ E9, 00, 00, 00, 00 ]
+ alignment: 16
+ references:
+ - kind: branch32
+ offset: 1
+ target: _bar
+undefined-atoms:
+ - name: _bar
+
+# Make sure that the driver issues an error diagnostic about this combination
+# being invalid.
+#
+# CHECK-WARNING: can't use -undefined warning or suppress with -twolevel_namespace
+# CHECK-SUPPRESS: can't use -undefined warning or suppress with -twolevel_namespace
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch arm64 -o %t %s \
+# RUN: %p/Inputs/unwind-info-simple-arm64.yaml -e _main %p/Inputs/arm64/libSystem.yaml
+# RUN: llvm-objdump --unwind-info %t | FileCheck %s
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0xE0, 0x03, 0x1E, 0x32, 0x00, 0x00, 0x00, 0x94,
+ 0x48, 0x01, 0x80, 0x52, 0x08, 0x00, 0x00, 0xB9,
+ 0x02, 0x00, 0x80, 0xD2, 0x01, 0x00, 0x00, 0x90,
+ 0x21, 0x00, 0x40, 0xF9, 0x00, 0x00, 0x00, 0x94,
+ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0xE0, 0x03, 0x1E, 0x32, 0x00, 0x00, 0x00, 0x94,
+ 0x48, 0x01, 0x80, 0x52, 0x08, 0x00, 0x00, 0xB9,
+ 0x02, 0x00, 0x80, 0xD2, 0x01, 0x00, 0x00, 0x90,
+ 0x21, 0x00, 0x40, 0xF9, 0x00, 0x00, 0x00, 0x94,
+ 0x3F, 0x04, 0x00, 0x71, 0x81, 0x00, 0x00, 0x54,
+ 0x00, 0x00, 0x00, 0x94, 0xFD, 0x7B, 0xC1, 0xA8,
+ 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x94,
+ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x94 ]
+ relocations:
+ - offset: 0x00000070
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000064
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 7
+ - offset: 0x00000060
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 12
+ - offset: 0x00000058
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 11
+ - offset: 0x0000004C
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 13
+ - offset: 0x00000048
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 8
+ - offset: 0x00000044
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 8
+ - offset: 0x00000034
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 10
+ - offset: 0x00000024
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 13
+ - offset: 0x00000020
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 8
+ - offset: 0x0000001C
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 8
+ - offset: 0x0000000C
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 10
+ - segment: __TEXT
+ section: __gcc_except_tab
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 2
+ address: 0x0000000000000074
+ content: [ 0xFF, 0x9B, 0xAF, 0x80, 0x00, 0x03, 0x27, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x01, 0x28, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0xD0, 0xFF, 0xFF, 0xFF ]
+ relocations:
+ - offset: 0x00000030
+ type: ARM64_RELOC_POINTER_TO_GOT
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 9
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ ]
+ alignment: 8
+ address: 0x00000000000000A8
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000040
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000038
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 2
+ - offset: 0x00000030
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: true
+ symbol: 14
+ - offset: 0x00000020
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: ltmp14
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000074
+ - name: GCC_except_table1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000074
+ - name: ltmp21
+ type: N_SECT
+ sect: 3
+ value: 0x00000000000000A8
+global-symbols:
+ - name: __Z3barv
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000028
+ - name: __Z3foov
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000068
+undefined-symbols:
+ - name: __Unwind_Resume
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: __ZTIi
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: __ZTIl
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_allocate_exception
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_begin_catch
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_end_catch
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___cxa_throw
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: ___gxx_personality_v0
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: Contents of __unwind_info section:
+# CHECK: Version: 0x1
+# CHECK: Common encodings array section offset: 0x1c
+# CHECK: Number of common encodings in array: 0x0
+# CHECK: Personality function array section offset: 0x1c
+# CHECK: Number of personality functions in array: 0x1
+# CHECK: Index array section offset: 0x20
+# CHECK: Number of indices in array: 0x2
+# CHECK: Common encodings: (count = 0)
+# CHECK: Personality functions: (count = 1)
+# CHECK: personality[1]: 0x00004020
+# CHECK: Top level indices: (count = 2)
+# CHECK: [0]: function offset=0x00003e68, 2nd level page offset=0x00000040, LSDA offset=0x00000038
+# CHECK: [1]: function offset=0x00003edc, 2nd level page offset=0x00000000, LSDA offset=0x00000040
+# CHECK: LSDA descriptors:
+# CHECK: [0]: function offset=0x00003e90, LSDA offset=0x00003f6c
+# CHECK: Second level indices:
+# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00003e68
+# CHECK: [0]: function offset=0x00003e68, encoding=0x04000000
+# CHECK: [1]: function offset=0x00003e90, encoding=0x54000000
+# CHECK: [2]: function offset=0x00003ed0, encoding=0x04000000
+# CHECK-NOT: Contents of __compact_unwind section
+
+
+
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -e _main %p/Inputs/x86_64/libSystem.yaml
+# RUN: llvm-objdump --unwind-info %t | FileCheck %s
+
+# CHECK: Contents of __unwind_info section:
+# CHECK: Version: 0x1
+# CHECK: Common encodings array section offset: 0x1c
+# CHECK: Number of common encodings in array: 0x0
+# CHECK: Personality function array section offset: 0x1c
+# CHECK: Number of personality functions in array: 0x1
+# CHECK: Index array section offset: 0x20
+# CHECK: Number of indices in array: 0x2
+# CHECK: Common encodings: (count = 0)
+# CHECK: Personality functions: (count = 1)
+# CHECK: personality[1]: 0x00001000
+# CHECK: Top level indices: (count = 2)
+# CHECK: [0]: function offset=0x00000efb, 2nd level page offset=0x00000040, LSDA offset=0x00000038
+# CHECK: [1]: function offset=0x00000f00, 2nd level page offset=0x00000000, LSDA offset=0x00000040
+# CHECK: LSDA descriptors:
+# CHECK: [0]: function offset=0x00000efb, LSDA offset=0x00000f00
+# CHECK: Second level indices:
+# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00000efb
+# CHECK: [0]: function offset=0x00000efb, encoding=0x51000000
+# CHECK: [1]: function offset=0x00000efc, encoding=0x01000000
+# CHECK: [2]: function offset=0x00000efd, encoding=0x04000018
+# CHECK: [3]: function offset=0x00000efe, encoding=0x04000040
+# CHECK: [4]: function offset=0x00000eff, encoding=0x00000000
+# CHECK-NOT: Contents of __compact_unwind section
+
+--- !native
+path: '<linker-internal>'
+defined-atoms:
+ - name: GCC_except_table1
+ type: unwind-lsda
+ content: [ FF, 9B, A2, 80, 80, 00, 03, 1A, 08, 00, 00, 00,
+ 05, 00, 00, 00, 1A, 00, 00, 00, 01, 0D, 00, 00,
+ 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00,
+ 04, 00, 00, 00 ]
+ - type: compact-unwind
+ content: [ 40, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
+ 00, 00, 00, 41, 00, 00, 00, 00, 00, 00, 00, 00,
+ E0, 00, 00, 00, 00, 00, 00, 00 ]
+ references:
+ - kind: pointer64Anon
+ offset: 0
+ target: __Z3barv
+ - kind: pointer64
+ offset: 16
+ target: ___gxx_personality_v0
+ - kind: pointer64Anon
+ offset: 24
+ target: GCC_except_table1
+ - type: compact-unwind
+ content: [ C0, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
+ 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00 ]
+ references:
+ - kind: pointer64Anon
+ offset: 0
+ target: _main
+ - type: compact-unwind
+ content: [ C1, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
+ 00, 00, 00, 04, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 00, 00, 00, 00, 00 ]
+ references:
+ - kind: pointer64Anon
+ offset: 0
+ target: _needsDwarfButNoCompactUnwind
+
+# Generic x86_64 CIE:
+ - name: LCIE
+ type: unwind-cfi
+ content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00,
+ 01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ]
+
+ - type: unwind-cfi
+ content: [ 24, 00, 00, 00, 1C, 00, 00, 00, C8, FE, FF, FF,
+ FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
+ 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+ 00, 00, 00, 00 ]
+ references:
+ - kind: unwindFDEToFunction
+ offset: 8
+ target: _needsDwarfButNoCompactUnwind
+ - kind: negDelta32
+ offset: 4
+ target: LCIE
+
+ - type: unwind-cfi
+ content: [ 24, 00, 00, 00, 44, 00, 00, 00, C8, FE, FF, FF,
+ FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
+ 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+ 00, 00, 00, 00 ]
+ references:
+ - kind: unwindFDEToFunction
+ offset: 8
+ target: _needsDwarfSaysCompactUnwind
+ - kind: negDelta32
+ offset: 4
+ target: LCIE
+
+ - type: unwind-cfi
+ content: [ 24, 00, 00, 00, 6C, 00, 00, 00, C8, FE, FF, FF,
+ FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
+ 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+ 00, 00, 00, 00 ]
+ references:
+ - kind: unwindFDEToFunction
+ offset: 8
+ target: _main
+ - kind: negDelta32
+ offset: 4
+ target: LCIE
+
+ - name: __Z3barv
+ scope: global
+ content: [ C3 ]
+ - name: _main
+ scope: global
+ content: [ C3 ]
+ - name: _needsDwarfButNoCompactUnwind
+ scope: global
+ content: [ C3 ]
+ - name: _needsDwarfSaysCompactUnwind
+ scope: global
+ content: [ C3 ]
+ - name: _noUnwindData
+ scope: global
+ content: [ C3 ]
+
+shared-library-atoms:
+ - name: ___gxx_personality_v0
+ load-name: '/usr/lib/libc++abi.dylib'
+ type: unknown
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %p/Inputs/bar.yaml \
+# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/x86_64/libSystem.yaml -o %t1.dylib
+# RUN: ld64.lld.darwinold -arch x86_64 -dylib %s -upward_library %t1.dylib \
+# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/x86_64/libSystem.yaml -o %t
+# RUN: llvm-objdump --private-headers %t | FileCheck %s
+#
+#
+# Test upward linking: 1) build libbar.dylib, 2) build libfoo.dylib and upward
+# like with libbar.dylib, 3) dump load commands of libfoo and verify upward link.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9,
+ 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000008
+ type: X86_64_RELOC_BRANCH
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _bar
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+...
+
+
+# CHECK: cmd LC_LOAD_UPWARD_DYLIB
+# CHECK-NEXT: cmdsize 48
+# CHECK-NEXT: name /usr/lib/libbar.dylib (offset 24)
--- /dev/null
+#
+#
+# RUN: ld64.lld.darwinold -arch x86_64 -r -test_file_usage -v \
+# RUN: -path_exists /Custom/Frameworks \
+# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \
+# RUN: -path_exists /usr/lib \
+# RUN: -path_exists /usr/lib/libfoo.dylib \
+# RUN: -path_exists /opt/stuff/libstuff.dylib \
+# RUN: -F/Custom/Frameworks \
+# RUN: -upward_framework Bar \
+# RUN: -upward-lfoo \
+# RUN: -upward_library /opt/stuff/libstuff.dylib \
+# RUN: 2>&1 | FileCheck %s
+
+# CHECK: Found upward framework /Custom/Frameworks/Bar.framework/Bar
+# CHECK: Found upward library /usr/lib/libfoo.dylib
+
+
--- /dev/null
+# RUN: not ld64.lld.darwinold | FileCheck %s
+#
+# Test that running darwin linker with no option prints out usage message.
+#
+
+
+# CHECK: USAGE:
+# CHECK: -arch
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s \
+# RUN: %p/Inputs/use-simple-dylib.yaml %p/Inputs/x86_64/libSystem.yaml -dylib -o %t.dylib
+# RUN: llvm-objdump --private-headers %t.dylib | FileCheck %s
+
+# This test ensures that we have a LC_LOAD_DYLIB for libspecial.dylib even though we don't
+# use any atoms from it. This matches the ld64 behaviour.
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00,
+ 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00,
+ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+# CHECK: cmd LC_LOAD_DYLIB
+# CHECK: name libspecial.dylib (offset 24)
+# CHECK: current version 1.0.0
+# CHECK: compatibility version 1.0.0
+# CHECK: cmd LC_LOAD_DYLIB
+# CHECK: name /usr/lib/libSystem.B.dylib (offset 24)
+# CHECK: current version 1.0.0
+# CHECK: compatibility version 1.0.0
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -print_atoms -r %s \
+# RUN: %p/Inputs/use-simple-dylib.yaml -o %t | FileCheck %s
+
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00,
+ 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00,
+ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _myGlobal
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myGlobalWeak
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myHidden
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myHiddenWeak
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myResolver
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myStatic
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _myVariablePreviouslyKnownAsPrivateExtern
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: undefined-atoms:
+# CHECK: - name: _myStatic
+# CHECK: - name: _myVariablePreviouslyKnownAsPrivateExtern
+# CHECK: shared-library-atoms:
+# CHECK: - name: _myGlobal
+# CHECK: load-name: libspecial.dylib
+# CHECK: - name: _myGlobalWeak
+# CHECK: load-name: libspecial.dylib
+# CHECK: - name: _myHidden
+# CHECK: load-name: libspecial.dylib
+# CHECK: - name: _myHiddenWeak
+# CHECK: load-name: libspecial.dylib
+# CHECK: - name: _myResolver
+# CHECK: load-name: libspecial.dylib
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -r -macosx_version_min 10.8 && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -r && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 %s -o %t -r %p/Inputs/no-version-min-load-command-object.yaml && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+
+# If we are emitting an object file, then we only emit a min version load command if the source object file(s) all have
+# version(s) and either known platforms or contain min version load commands themselves.
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+min-os-version-kind: LC_VERSION_MIN_MACOSX
+min-os-version: 10.8
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: Load command {{[0-9]*}}
+# CHECK: cmd LC_VERSION_MIN_MACOSX
+# CHECK: cmdsize 16
+# CHECK: version 10.8
+# CHECK: sdk n/a
+
+# NO_VERSION_MIN-NOT: LC_VERSION_MIN_MACOSX
\ No newline at end of file
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml 2>&1 | FileCheck %s --check-prefix=WARNING
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -version_load_command && llvm-objdump --private-headers %t | FileCheck %s
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_version_load_command && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -version_load_command -no_version_load_command && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+
+# RUN: ld64.lld.darwinold -arch x86_64 -macosx_version_min 10.8 -sdk_version 10.9 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump --private-headers %t | FileCheck %s --check-prefix=SDK_VERSION
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+# CHECK: Load command {{[0-9]*}}
+# CHECK: cmd LC_VERSION_MIN_MACOSX
+# CHECK: cmdsize 16
+# CHECK: version 10.8
+# CHECK: sdk 10.8
+
+# SDK_VERSION: Load command {{[0-9]*}}
+# SDK_VERSION: cmd LC_VERSION_MIN_MACOSX
+# SDK_VERSION: cmdsize 16
+# SDK_VERSION: version 10.8
+# SDK_VERSION: sdk 10.9
+
+# WARNING: warning: -sdk_version is required when emitting min version load command. Setting sdk version to match provided min version
+
+# NO_VERSION_MIN-NOT: LC_VERSION_MIN_MACOSX
--- /dev/null
+# RUN: ld64.lld.darwinold -arch x86_64 %s %p/Inputs/write-final-sections.yaml \
+# RUN: -o %t -e _foo
+# RUN: llvm-readobj --sections --section-data %t | FileCheck %s
+
+--- !native
+defined-atoms:
+# For __TEXT, __text (with typeCode)
+ - name: _foo
+ scope: global
+ content: [ 55 ]
+# CHECK: Name: __text
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 55
+# CHECK-NEXT: )
+
+# For __TEXT, __const (with typeConstant),
+ - type: constant
+ content: [ 01, 00, 00, 00 ]
+# From __TEXT, __literal4, (with typeLiteral4)
+ - scope: hidden
+ type: const-4-byte
+ content: [ 02, 00, 00, 00 ]
+# From __TEXT, __literal8, (with typeLiteral8)
+ - scope: hidden
+ type: const-8-byte
+ content: [ 03, 00, 00, 00, 00, 00, 00, 00 ]
+# From __TEXT, __literal16, (with typeLiteral16)
+ - scope: hidden
+ type: const-16-byte
+ content: [ 04, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: Name: __const
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 01000000 02000000 03000000 00000000
+# CHECK-NEXT: 0010: 04000000 00000000 00000000 00000000
+# CHECK-NEXT: )
+
+# For __TEXT, __cstring (with typeCString)
+ - scope: hidden
+ type: c-string
+ content: [ 57, 69, 62, 62, 6C, 65, 00 ]
+ merge: by-content
+# CHECK: Name: __cstring
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 57696262 6C6500
+# CHECK-NEXT: )
+
+# For __TEXT, __ustring (with typeUTF16String)
+ - scope: hidden
+ type: utf16-string
+ content: [ 05, 00 ]
+ merge: by-content
+# CHECK: Name: __ustring
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0500
+# CHECK-NEXT: )
+
+# For __TEXT, __gcc_except_tab, (with typeLSDA)
+ - name: GCC_except_table0
+ type: unwind-lsda
+ content: [ 06, 00 ]
+# CHECK: Name: __gcc_except_tab
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0600
+# CHECK-NEXT: )
+
+# For __TEXT, __eh_frame, (with typeCFI)
+ - name: LCIE
+ type: unwind-cfi
+ content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00,
+ 01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ]
+
+ - type: unwind-cfi
+ content: [ 24, 00, 00, 00, 1C, 00, 00, 00, C8, FE, FF, FF,
+ FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00,
+ 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00,
+ 00, 00, 00, 00 ]
+ references:
+ - kind: unwindFDEToFunction
+ offset: 8
+ target: _foo
+ - kind: negDelta32
+ offset: 4
+ target: LCIE
+
+# CHECK: Name: __eh_frame
+# CHECK: Segment: __TEXT
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 14000000 00000000 017A5200 01781001
+# CHECK-NEXT: 0010: 100C0708 90010000 24000000 1C000000
+# CHECK-NEXT: 0020: 70FFFFFF FFFFFFFF 01000000 00000000
+# CHECK-NEXT: 0030: 00410E10 8602430D 06000000 00000000
+# CHECK-NEXT: )
+
+# For __DATA, __data, (with typeData)
+ - name: var
+ type: data
+ content: [ 08 ]
+# CHECK: Name: __data
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 08
+# CHECK-NEXT: )
+
+# For __DATA, __bss (with typeZeroFill)
+# FIXME: Attributes & tags of __bss are mostly broken. Should be at end of
+# __DATA, should have size, should have S_ZEROFILL flag.
+ - type: zero-fill
+ size: 8
+# CHECK: Name: __bss
+# CHECK: Segment: __DATA
+
+# For __DATA, __const, (with typeConstData)
+ - type: const-data
+ content: [ 09, 00, 00, 00 ]
+# CHECK: Name: __const
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 09000000
+# CHECK-NEXT: )
+
+# For __DATA, __cfstring, (with typeCFString)
+ - type: cfstring
+ content: [ 0A, 00 ]
+# CHECK: Name: __cfstring
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0A00
+# CHECK-NEXT: )
+
+# For __DATA, __got (with typeGOT)
+ - type: got
+ content: [ 0B, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: Name: __got
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0B000000 00000000
+# CHECK-NEXT: )
+
+
+# For __DATA, __mod_init_func (with typeInitializerPtr)
+ - type: initializer-pointer
+ content: [ 0C, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: Name: __mod_init_func
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0C000000 00000000
+# CHECK-NEXT: )
+
+# For __DATA, __mod_term_func (with typeTerminatorPointer)
+ - type: terminator-pointer
+ content: [ 0D, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK: Name: __mod_term_func
+# CHECK: Segment: __DATA
+# CHECK: SectionData (
+# CHECK-NEXT: 0000: 0D000000 00000000
+# CHECK-NEXT: )
+
+ - type: compact-unwind
+ content: [ 0E, 00, 00, 00, 00, 00, 00, 00 ]
+# CHECK-NOT: Name: __compact_unwind
--- /dev/null
+# RUN: not ld64.lld.darwinold -arch x86_64 -r %s \
+# RUN: %p/Inputs/wrong-arch-error.yaml 2> %t.err
+# RUN: FileCheck %s < %t.err
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0xCC ]
+
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+...
+
+
+# CHECK: wrong architecture
--- /dev/null
+add_custom_target(LLDUnitTests)
+set_target_properties(LLDUnitTests PROPERTIES FOLDER "lld tests")
+
+set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
+
+# add_lld_unittest(test_dirname file1.cpp file2.cpp)
+#
+# Will compile the list of files together and link against lld
+# Produces a binary named 'basename(test_dirname)'.
+function(add_lld_unittest test_dirname)
+ add_unittest(LLDUnitTests ${test_dirname} ${ARGN})
+ target_link_libraries(${test_dirname} ${LLVM_COMMON_LIBS})
+endfunction()
+
+add_subdirectory(DriverTests)
+add_subdirectory(MachOTests)
--- /dev/null
+add_lld_unittest(DriverTests
+ DarwinLdDriverTest.cpp
+ )
+
+target_link_libraries(DriverTests
+ PRIVATE
+ lldDriver
+ lldMachOOld
+ )
--- /dev/null
+//===- lld/unittest/DarwinLdDriverTest.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Darwin's ld driver tests.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Driver.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace lld;
+
+namespace lld {
+namespace mach_o {
+bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx);
+}
+}
+
+namespace {
+class DarwinLdParserTest : public testing::Test {
+protected:
+ int inputFileCount() { return _ctx.getNodes().size(); }
+
+ StringRef inputFile(int index) {
+ Node &node = *_ctx.getNodes()[index];
+ if (node.kind() == Node::Kind::File)
+ return cast<FileNode>(&node)->getFile()->path();
+ llvm_unreachable("not handling other types of input files");
+ }
+
+ bool parse(std::vector<const char *> args) {
+ args.insert(args.begin(), "ld");
+ return mach_o::parse(args, _ctx);
+ }
+
+ MachOLinkingContext _ctx;
+};
+}
+
+TEST_F(DarwinLdParserTest, Basic) {
+ EXPECT_TRUE(parse({"foo.o", "bar.o", "-arch", "i386"}));
+ EXPECT_FALSE(_ctx.allowRemainingUndefines());
+ EXPECT_FALSE(_ctx.deadStrip());
+ EXPECT_EQ(2, inputFileCount());
+ EXPECT_EQ("foo.o", inputFile(0));
+ EXPECT_EQ("bar.o", inputFile(1));
+}
+
+TEST_F(DarwinLdParserTest, Output) {
+ EXPECT_TRUE(parse({"-o", "my.out", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ("my.out", _ctx.outputPath());
+}
+
+TEST_F(DarwinLdParserTest, Dylib) {
+ EXPECT_TRUE(parse({"-dylib", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ(llvm::MachO::MH_DYLIB, _ctx.outputMachOType());
+}
+
+TEST_F(DarwinLdParserTest, Relocatable) {
+ EXPECT_TRUE(parse({"-r", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ(llvm::MachO::MH_OBJECT, _ctx.outputMachOType());
+}
+
+TEST_F(DarwinLdParserTest, Bundle) {
+ EXPECT_TRUE(parse({"-bundle", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ(llvm::MachO::MH_BUNDLE, _ctx.outputMachOType());
+}
+
+TEST_F(DarwinLdParserTest, Preload) {
+ EXPECT_TRUE(parse({"-preload", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ(llvm::MachO::MH_PRELOAD, _ctx.outputMachOType());
+}
+
+TEST_F(DarwinLdParserTest, Static) {
+ EXPECT_TRUE(parse({"-static", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ(llvm::MachO::MH_EXECUTE, _ctx.outputMachOType());
+}
+
+TEST_F(DarwinLdParserTest, Entry) {
+ EXPECT_TRUE(parse({"-e", "entryFunc", "foo.o", "-arch", "i386"}));
+ EXPECT_EQ("entryFunc", _ctx.entrySymbolName());
+}
+
+TEST_F(DarwinLdParserTest, DeadStrip) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "-dead_strip", "foo.o"}));
+ EXPECT_TRUE(_ctx.deadStrip());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsExe) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "-dead_strip", "foo.o"}));
+ EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsDylib) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "-dylib", "-dead_strip", "foo.o"}));
+ EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsRelocatable) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "-r", "-dead_strip", "foo.o"}));
+ EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicExe) {
+ EXPECT_TRUE(
+ parse({"-arch", "x86_64", "-dead_strip", "-export_dynamic", "foo.o"}));
+ EXPECT_TRUE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicDylib) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "-dylib", "-dead_strip",
+ "-export_dynamic", "foo.o"}));
+ EXPECT_TRUE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, DeadStripRootsExportDynamicRelocatable) {
+ EXPECT_TRUE(parse(
+ {"-arch", "x86_64", "-r", "-dead_strip", "-export_dynamic", "foo.o"}));
+ EXPECT_FALSE(_ctx.globalsAreDeadStripRoots());
+}
+
+TEST_F(DarwinLdParserTest, Arch) {
+ EXPECT_TRUE(parse({"-arch", "x86_64", "foo.o"}));
+ EXPECT_EQ(MachOLinkingContext::arch_x86_64, _ctx.arch());
+ EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_X86_64, _ctx.getCPUType());
+ EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_64_ALL, _ctx.getCPUSubType());
+}
+
+TEST_F(DarwinLdParserTest, Arch_x86) {
+ EXPECT_TRUE(parse({"-arch", "i386", "foo.o"}));
+ EXPECT_EQ(MachOLinkingContext::arch_x86, _ctx.arch());
+ EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_I386, _ctx.getCPUType());
+ EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_ALL, _ctx.getCPUSubType());
+}
+
+TEST_F(DarwinLdParserTest, Arch_armv6) {
+ EXPECT_TRUE(parse({"-arch", "armv6", "foo.o"}));
+ EXPECT_EQ(MachOLinkingContext::arch_armv6, _ctx.arch());
+ EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
+ EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V6, _ctx.getCPUSubType());
+}
+
+TEST_F(DarwinLdParserTest, Arch_armv7) {
+ EXPECT_TRUE(parse({"-arch", "armv7", "foo.o"}));
+ EXPECT_EQ(MachOLinkingContext::arch_armv7, _ctx.arch());
+ EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
+ EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7, _ctx.getCPUSubType());
+}
+
+TEST_F(DarwinLdParserTest, Arch_armv7s) {
+ EXPECT_TRUE(parse({"-arch", "armv7s", "foo.o"}));
+ EXPECT_EQ(MachOLinkingContext::arch_armv7s, _ctx.arch());
+ EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType());
+ EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7S, _ctx.getCPUSubType());
+}
+
+TEST_F(DarwinLdParserTest, MinMacOSX10_7) {
+ EXPECT_TRUE(
+ parse({"-macosx_version_min", "10.7", "foo.o", "-arch", "x86_64"}));
+ EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("10.7", ""));
+ EXPECT_FALSE(_ctx.minOS("10.8", ""));
+}
+
+TEST_F(DarwinLdParserTest, MinMacOSX10_8) {
+ EXPECT_TRUE(
+ parse({"-macosx_version_min", "10.8.3", "foo.o", "-arch", "x86_64"}));
+ EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("10.7", ""));
+ EXPECT_TRUE(_ctx.minOS("10.8", ""));
+}
+
+TEST_F(DarwinLdParserTest, iOS5) {
+ EXPECT_TRUE(parse({"-ios_version_min", "5.0", "foo.o", "-arch", "armv7"}));
+ EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("", "5.0"));
+ EXPECT_FALSE(_ctx.minOS("", "6.0"));
+}
+
+TEST_F(DarwinLdParserTest, iOS6) {
+ EXPECT_TRUE(parse({"-ios_version_min", "6.0", "foo.o", "-arch", "armv7"}));
+ EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("", "5.0"));
+ EXPECT_TRUE(_ctx.minOS("", "6.0"));
+}
+
+TEST_F(DarwinLdParserTest, iOS_Simulator5) {
+ EXPECT_TRUE(
+ parse({"-ios_simulator_version_min", "5.0", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("", "5.0"));
+ EXPECT_FALSE(_ctx.minOS("", "6.0"));
+}
+
+TEST_F(DarwinLdParserTest, iOS_Simulator6) {
+ EXPECT_TRUE(
+ parse({"-ios_simulator_version_min", "6.0", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os());
+ EXPECT_TRUE(_ctx.minOS("", "5.0"));
+ EXPECT_TRUE(_ctx.minOS("", "6.0"));
+}
+
+TEST_F(DarwinLdParserTest, compatibilityVersion) {
+ EXPECT_TRUE(parse(
+ {"-dylib", "-compatibility_version", "1.2.3", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(_ctx.compatibilityVersion(), 0x10203U);
+}
+
+TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) {
+ EXPECT_FALSE(parse(
+ {"-bundle", "-compatibility_version", "1.2.3", "a.o", "-arch", "i386"}));
+}
+
+TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) {
+ EXPECT_FALSE(parse(
+ {"-bundle", "-compatibility_version", "1,2,3", "a.o", "-arch", "i386"}));
+}
+
+TEST_F(DarwinLdParserTest, currentVersion) {
+ EXPECT_TRUE(
+ parse({"-dylib", "-current_version", "1.2.3", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(_ctx.currentVersion(), 0x10203U);
+}
+
+TEST_F(DarwinLdParserTest, currentVersionInvalidType) {
+ EXPECT_FALSE(
+ parse({"-bundle", "-current_version", "1.2.3", "a.o", "-arch", "i386"}));
+}
+
+TEST_F(DarwinLdParserTest, currentVersionInvalidValue) {
+ EXPECT_FALSE(
+ parse({"-bundle", "-current_version", "1,2,3", "a.o", "-arch", "i386"}));
+}
+
+TEST_F(DarwinLdParserTest, bundleLoader) {
+ EXPECT_TRUE(
+ parse({"-bundle", "-bundle_loader", "/bin/ls", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(_ctx.bundleLoader(), "/bin/ls");
+}
+
+TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) {
+ EXPECT_FALSE(parse({"-bundle_loader", "/bin/ls", "a.o", "-arch", "i386"}));
+}
+
+TEST_F(DarwinLdParserTest, deadStrippableDylib) {
+ EXPECT_TRUE(
+ parse({"-dylib", "-mark_dead_strippable_dylib", "a.o", "-arch", "i386"}));
+ EXPECT_EQ(true, _ctx.deadStrippableDylib());
+}
+
+TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) {
+ EXPECT_FALSE(parse({"-mark_dead_strippable_dylib", "a.o", "-arch", "i386"}));
+}
--- /dev/null
+
+add_lld_unittest(lldMachOOldTests
+ MachONormalizedFileBinaryReaderTests.cpp
+ MachONormalizedFileBinaryWriterTests.cpp
+ MachONormalizedFileToAtomsTests.cpp
+ MachONormalizedFileYAMLTests.cpp
+ )
+
+target_link_libraries(lldMachOOldTests
+ PRIVATE
+ lldDriver
+ lldMachOOld
+ lldYAML
+ )
--- /dev/null
+//===- lld/unittest/MachOTests/MachONormalizedFileBinaryReaderTests.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <memory>
+
+using llvm::SmallString;
+using llvm::StringRef;
+using llvm::MemoryBuffer;
+using llvm::Twine;
+
+using namespace lld::mach_o::normalized;
+using namespace llvm::MachO;
+
+static std::unique_ptr<NormalizedFile>
+fromBinary(const uint8_t bytes[], unsigned length, StringRef archStr) {
+ StringRef sr((const char*)bytes, length);
+ std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(sr, "", false));
+ llvm::Expected<std::unique_ptr<NormalizedFile>> r =
+ lld::mach_o::normalized::readBinary(
+ mb, lld::MachOLinkingContext::archFromName(archStr));
+ EXPECT_FALSE(!r);
+ return std::move(*r);
+}
+
+// The Mach-O object reader uses functions such as read32 or read64
+// which don't allow unaligned access. Our in-memory object file
+// needs to be aligned to a larger boundary than uint8_t's.
+#if _MSC_VER
+#define FILEBYTES __declspec(align(64)) const uint8_t fileBytes[]
+#else
+#define FILEBYTES const uint8_t fileBytes[] __attribute__((aligned(64)))
+#endif
+
+TEST(BinaryReaderTest, empty_obj_x86_64) {
+ FILEBYTES = {
+ 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(BinaryReaderTest, empty_obj_x86) {
+ FILEBYTES = {
+ 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "i386");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(BinaryReaderTest, empty_obj_ppc) {
+ FILEBYTES = {
+ 0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00, 0x00, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "ppc");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(BinaryReaderTest, empty_obj_armv7) {
+ FILEBYTES = {
+ 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "armv7");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(BinaryReaderTest, empty_obj_x86_64_arm7) {
+ FILEBYTES = {
+#include "empty_obj_x86_armv7.txt"
+ };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+
+ std::unique_ptr<NormalizedFile> f2 =
+ fromBinary(fileBytes, sizeof(fileBytes), "armv7");
+ EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_armv7);
+ EXPECT_EQ((int)(f2->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f2->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_TRUE(f2->globalSymbols.empty());
+ EXPECT_TRUE(f2->undefinedSymbols.empty());
+}
+
+TEST(BinaryReaderTest, hello_obj_x86_64) {
+ FILEBYTES = {
+ 0xCF, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x01,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x70, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0xA4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xB4, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xE4, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10,
+ 0x48, 0x8D, 0x3D, 0x00, 0x00, 0x00, 0x00, 0xC7,
+ 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00,
+ 0xE8, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00,
+ 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x48,
+ 0x83, 0xC4, 0x10, 0x5D, 0xC3, 0x68, 0x65, 0x6C,
+ 0x6C, 0x6F, 0x0A, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x2D, 0x0B, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1D, 0x0F, 0x00, 0x00, 0x00,
+ 0x0E, 0x02, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61,
+ 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E,
+ 0x74, 0x66, 0x00, 0x4C, 0x5F, 0x2E, 0x73, 0x74,
+ 0x72, 0x00, 0x00, 0x00 };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
+
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+ const Section& text = f->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(text.type, S_REGULAR);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 16U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(text.content.size(), 45UL);
+ EXPECT_EQ((int)(text.content[0]), 0x55);
+ EXPECT_EQ((int)(text.content[1]), 0x48);
+ EXPECT_TRUE(text.indirectSymbols.empty());
+ EXPECT_EQ(text.relocations.size(), 2UL);
+ const Relocation& call = text.relocations[0];
+ EXPECT_EQ(call.offset, Hex32(0x19));
+ EXPECT_EQ(call.type, X86_64_RELOC_BRANCH);
+ EXPECT_EQ(call.length, 2);
+ EXPECT_EQ(call.isExtern, true);
+ EXPECT_EQ(call.symbol, 2U);
+ const Relocation& str = text.relocations[1];
+ EXPECT_EQ(str.offset, Hex32(0xB));
+ EXPECT_EQ(str.type, X86_64_RELOC_SIGNED);
+ EXPECT_EQ(str.length, 2);
+ EXPECT_EQ(str.isExtern, true);
+ EXPECT_EQ(str.symbol, 0U);
+
+ const Section& cstring = f->sections[1];
+ EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
+ EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
+ EXPECT_EQ(cstring.attributes, SectionAttr(0));
+ EXPECT_EQ((uint16_t)cstring.alignment, 1U);
+ EXPECT_EQ(cstring.address, Hex64(0x02D));
+ EXPECT_EQ(cstring.content.size(), 7UL);
+ EXPECT_EQ((int)(cstring.content[0]), 0x68);
+ EXPECT_EQ((int)(cstring.content[1]), 0x65);
+ EXPECT_EQ((int)(cstring.content[2]), 0x6c);
+ EXPECT_TRUE(cstring.indirectSymbols.empty());
+ EXPECT_TRUE(cstring.relocations.empty());
+
+ EXPECT_EQ(f->localSymbols.size(), 1UL);
+ const Symbol& strLabel = f->localSymbols[0];
+ EXPECT_EQ(strLabel.type, N_SECT);
+ EXPECT_EQ(strLabel.sect, 2);
+ EXPECT_EQ(strLabel.value, Hex64(0x2D));
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& mainLabel = f->globalSymbols[0];
+ EXPECT_TRUE(mainLabel.name.equals("_main"));
+ EXPECT_EQ(mainLabel.type, N_SECT);
+ EXPECT_EQ(mainLabel.sect, 1);
+ EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
+ EXPECT_EQ(mainLabel.value, Hex64(0x0));
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& printfLabel = f->undefinedSymbols[0];
+ EXPECT_TRUE(printfLabel.name.equals("_printf"));
+ EXPECT_EQ(printfLabel.type, N_UNDF);
+ EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
+}
+
+TEST(BinaryReaderTest, hello_obj_x86) {
+ FILEBYTES = {
+ 0xCE, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xE5, 0x83,
+ 0xEC, 0x18, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x58,
+ 0x8D, 0x80, 0x25, 0x00, 0x00, 0x00, 0xC7, 0x45,
+ 0xFC, 0x00, 0x00, 0x00, 0x00, 0x89, 0x04, 0x24,
+ 0xE8, 0xDF, 0xFF, 0xFF, 0xFF, 0xB9, 0x00, 0x00,
+ 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x83,
+ 0xC4, 0x18, 0x5D, 0xC3, 0x68, 0x65, 0x6C, 0x6C,
+ 0x6F, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x0D, 0x0E, 0x00, 0x00, 0xA4,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1,
+ 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61,
+ 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E,
+ 0x74, 0x66, 0x00, 0x00
+ };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "i386");
+
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+ const Section& text = f->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(text.type, S_REGULAR);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 16U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(text.content.size(), 48UL);
+ EXPECT_EQ((int)(text.content[0]), 0x55);
+ EXPECT_EQ((int)(text.content[1]), 0x89);
+ EXPECT_TRUE(text.indirectSymbols.empty());
+ EXPECT_EQ(text.relocations.size(), 3UL);
+ const Relocation& call = text.relocations[0];
+ EXPECT_EQ(call.offset, Hex32(0x1D));
+ EXPECT_EQ(call.scattered, false);
+ EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA);
+ EXPECT_EQ(call.pcRel, true);
+ EXPECT_EQ(call.length, 2);
+ EXPECT_EQ(call.isExtern, true);
+ EXPECT_EQ(call.symbol, 1U);
+ const Relocation& sectDiff = text.relocations[1];
+ EXPECT_EQ(sectDiff.offset, Hex32(0xE));
+ EXPECT_EQ(sectDiff.scattered, true);
+ EXPECT_EQ(sectDiff.type, GENERIC_RELOC_LOCAL_SECTDIFF);
+ EXPECT_EQ(sectDiff.pcRel, false);
+ EXPECT_EQ(sectDiff.length, 2);
+ EXPECT_EQ(sectDiff.value, 0x30U);
+ const Relocation& pair = text.relocations[2];
+ EXPECT_EQ(pair.offset, Hex32(0x0));
+ EXPECT_EQ(pair.scattered, true);
+ EXPECT_EQ(pair.type, GENERIC_RELOC_PAIR);
+ EXPECT_EQ(pair.pcRel, false);
+ EXPECT_EQ(pair.length, 2);
+ EXPECT_EQ(pair.value, 0x0BU);
+
+ const Section& cstring = f->sections[1];
+ EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
+ EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
+ EXPECT_EQ(cstring.attributes, SectionAttr(0));
+ EXPECT_EQ((uint16_t)cstring.alignment, 1U);
+ EXPECT_EQ(cstring.address, Hex64(0x030));
+ EXPECT_EQ(cstring.content.size(), 7UL);
+ EXPECT_EQ((int)(cstring.content[0]), 0x68);
+ EXPECT_EQ((int)(cstring.content[1]), 0x65);
+ EXPECT_EQ((int)(cstring.content[2]), 0x6c);
+ EXPECT_TRUE(cstring.indirectSymbols.empty());
+ EXPECT_TRUE(cstring.relocations.empty());
+
+ EXPECT_EQ(f->localSymbols.size(), 0UL);
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& mainLabel = f->globalSymbols[0];
+ EXPECT_TRUE(mainLabel.name.equals("_main"));
+ EXPECT_EQ(mainLabel.type, N_SECT);
+ EXPECT_EQ(mainLabel.sect, 1);
+ EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
+ EXPECT_EQ(mainLabel.value, Hex64(0x0));
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& printfLabel = f->undefinedSymbols[0];
+ EXPECT_TRUE(printfLabel.name.equals("_printf"));
+ EXPECT_EQ(printfLabel.type, N_UNDF);
+ EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
+}
+
+TEST(BinaryReaderTest, hello_obj_armv7) {
+ FILEBYTES = {
+ 0xCE, 0xFA, 0xED, 0xFE, 0x0C, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
+ 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x6E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0xB5, 0x6F, 0x46,
+ 0x82, 0xB0, 0x40, 0xF2, 0x18, 0x00, 0xC0, 0xF2,
+ 0x00, 0x00, 0x78, 0x44, 0x00, 0x21, 0xC0, 0xF2,
+ 0x00, 0x01, 0x01, 0x91, 0xFF, 0xF7, 0xF2, 0xFF,
+ 0x00, 0x21, 0xC0, 0xF2, 0x00, 0x01, 0x00, 0x90,
+ 0x08, 0x46, 0x02, 0xB0, 0x80, 0xBD, 0x68, 0x65,
+ 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x6D,
+ 0x0A, 0x00, 0x00, 0xB9, 0x2A, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0xB1, 0x0E, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0xA9, 0x2A, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xA1, 0x0E, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F,
+ 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00
+ };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "armv7");
+
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+ const Section& text = f->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(text.type, S_REGULAR);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 4U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(text.content.size(), 42UL);
+ EXPECT_EQ((int)(text.content[0]), 0x80);
+ EXPECT_EQ((int)(text.content[1]), 0xB5);
+ EXPECT_TRUE(text.indirectSymbols.empty());
+ EXPECT_EQ(text.relocations.size(), 5UL);
+ const Relocation& call = text.relocations[0];
+ EXPECT_EQ(call.offset, Hex32(0x18));
+ EXPECT_EQ(call.scattered, false);
+ EXPECT_EQ(call.type, ARM_THUMB_RELOC_BR22);
+ EXPECT_EQ(call.length, 2);
+ EXPECT_EQ(call.isExtern, true);
+ EXPECT_EQ(call.symbol, 1U);
+ const Relocation& movt = text.relocations[1];
+ EXPECT_EQ(movt.offset, Hex32(0xA));
+ EXPECT_EQ(movt.scattered, true);
+ EXPECT_EQ(movt.type, ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(movt.length, 3);
+ EXPECT_EQ(movt.value, Hex32(0x2A));
+ const Relocation& movtPair = text.relocations[2];
+ EXPECT_EQ(movtPair.offset, Hex32(0x18));
+ EXPECT_EQ(movtPair.scattered, true);
+ EXPECT_EQ(movtPair.type, ARM_RELOC_PAIR);
+ EXPECT_EQ(movtPair.length, 3);
+ EXPECT_EQ(movtPair.value, Hex32(0xE));
+ const Relocation& movw = text.relocations[3];
+ EXPECT_EQ(movw.offset, Hex32(0x6));
+ EXPECT_EQ(movw.scattered, true);
+ EXPECT_EQ(movw.type, ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(movw.length, 2);
+ EXPECT_EQ(movw.value, Hex32(0x2A));
+ const Relocation& movwPair = text.relocations[4];
+ EXPECT_EQ(movwPair.offset, Hex32(0x0));
+ EXPECT_EQ(movwPair.scattered, true);
+ EXPECT_EQ(movwPair.type, ARM_RELOC_PAIR);
+ EXPECT_EQ(movwPair.length, 2);
+ EXPECT_EQ(movwPair.value, Hex32(0xE));
+
+ const Section& cstring = f->sections[1];
+ EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
+ EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
+ EXPECT_EQ(cstring.attributes, SectionAttr(0));
+ EXPECT_EQ((uint16_t)cstring.alignment, 1U);
+ EXPECT_EQ(cstring.address, Hex64(0x02A));
+ EXPECT_EQ(cstring.content.size(), 7UL);
+ EXPECT_EQ((int)(cstring.content[0]), 0x68);
+ EXPECT_EQ((int)(cstring.content[1]), 0x65);
+ EXPECT_EQ((int)(cstring.content[2]), 0x6c);
+ EXPECT_TRUE(cstring.indirectSymbols.empty());
+ EXPECT_TRUE(cstring.relocations.empty());
+
+ EXPECT_EQ(f->localSymbols.size(), 0UL);
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& mainLabel = f->globalSymbols[0];
+ EXPECT_TRUE(mainLabel.name.equals("_main"));
+ EXPECT_EQ(mainLabel.type, N_SECT);
+ EXPECT_EQ(mainLabel.sect, 1);
+ EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
+ EXPECT_EQ(mainLabel.value, Hex64(0x0));
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& printfLabel = f->undefinedSymbols[0];
+ EXPECT_TRUE(printfLabel.name.equals("_printf"));
+ EXPECT_EQ(printfLabel.type, N_UNDF);
+ EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
+}
+
+TEST(BinaryReaderTest, hello_obj_ppc) {
+ FILEBYTES = {
+ 0xFE, 0xED, 0xFA, 0xCE, 0x00, 0x00, 0x00, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x28,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x01, 0x44,
+ 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65,
+ 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45,
+ 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x44,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x90,
+ 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E,
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0xB8,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xD0,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7C, 0x08, 0x02, 0xA6,
+ 0xBF, 0xC1, 0xFF, 0xF8, 0x90, 0x01, 0x00, 0x08,
+ 0x94, 0x21, 0xFF, 0xB0, 0x7C, 0x3E, 0x0B, 0x78,
+ 0x42, 0x9F, 0x00, 0x05, 0x7F, 0xE8, 0x02, 0xA6,
+ 0x3C, 0x5F, 0x00, 0x00, 0x38, 0x62, 0x00, 0x2C,
+ 0x4B, 0xFF, 0xFF, 0xDD, 0x38, 0x00, 0x00, 0x00,
+ 0x7C, 0x03, 0x03, 0x78, 0x80, 0x21, 0x00, 0x00,
+ 0x80, 0x01, 0x00, 0x08, 0x7C, 0x08, 0x03, 0xA6,
+ 0xBB, 0xC1, 0xFF, 0xF8, 0x4E, 0x80, 0x00, 0x20,
+ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0xD3,
+ 0xAB, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x44,
+ 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0xAC, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x44,
+ 0xA1, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x01, 0x0F, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F,
+ 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00
+ };
+ std::unique_ptr<NormalizedFile> f =
+ fromBinary(fileBytes, sizeof(fileBytes), "ppc");
+
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
+ EXPECT_EQ((int)(f->fileType), MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+ const Section& text = f->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(text.type, S_REGULAR);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 4U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(text.content.size(), 68UL);
+ EXPECT_EQ((int)(text.content[0]), 0x7C);
+ EXPECT_EQ((int)(text.content[1]), 0x08);
+ EXPECT_TRUE(text.indirectSymbols.empty());
+ EXPECT_EQ(text.relocations.size(), 5UL);
+ const Relocation& bl = text.relocations[0];
+ EXPECT_EQ(bl.offset, Hex32(0x24));
+ EXPECT_EQ(bl.type, PPC_RELOC_BR24);
+ EXPECT_EQ(bl.length, 2);
+ EXPECT_EQ(bl.isExtern, true);
+ EXPECT_EQ(bl.symbol, 1U);
+ const Relocation& lo = text.relocations[1];
+ EXPECT_EQ(lo.offset, Hex32(0x20));
+ EXPECT_EQ(lo.scattered, true);
+ EXPECT_EQ(lo.type, PPC_RELOC_LO16_SECTDIFF);
+ EXPECT_EQ(lo.length, 2);
+ EXPECT_EQ(lo.value, Hex32(0x44));
+ const Relocation& loPair = text.relocations[2];
+ EXPECT_EQ(loPair.offset, Hex32(0x0));
+ EXPECT_EQ(loPair.scattered, true);
+ EXPECT_EQ(loPair.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(loPair.length, 2);
+ EXPECT_EQ(loPair.value, Hex32(0x18));
+ const Relocation& ha = text.relocations[3];
+ EXPECT_EQ(ha.offset, Hex32(0x1C));
+ EXPECT_EQ(ha.scattered, true);
+ EXPECT_EQ(ha.type, PPC_RELOC_HA16_SECTDIFF);
+ EXPECT_EQ(ha.length, 2);
+ EXPECT_EQ(ha.value, Hex32(0x44));
+ const Relocation& haPair = text.relocations[4];
+ EXPECT_EQ(haPair.offset, Hex32(0x2c));
+ EXPECT_EQ(haPair.scattered, true);
+ EXPECT_EQ(haPair.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(haPair.length, 2);
+ EXPECT_EQ(haPair.value, Hex32(0x18));
+
+ const Section& cstring = f->sections[1];
+ EXPECT_TRUE(cstring.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(cstring.sectionName.equals("__cstring"));
+ EXPECT_EQ(cstring.type, S_CSTRING_LITERALS);
+ EXPECT_EQ(cstring.attributes, SectionAttr(0));
+ EXPECT_EQ((uint16_t)cstring.alignment, 4U);
+ EXPECT_EQ(cstring.address, Hex64(0x044));
+ EXPECT_EQ(cstring.content.size(), 7UL);
+ EXPECT_EQ((int)(cstring.content[0]), 0x68);
+ EXPECT_EQ((int)(cstring.content[1]), 0x65);
+ EXPECT_EQ((int)(cstring.content[2]), 0x6c);
+ EXPECT_TRUE(cstring.indirectSymbols.empty());
+ EXPECT_TRUE(cstring.relocations.empty());
+
+ EXPECT_EQ(f->localSymbols.size(), 0UL);
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& mainLabel = f->globalSymbols[0];
+ EXPECT_TRUE(mainLabel.name.equals("_main"));
+ EXPECT_EQ(mainLabel.type, N_SECT);
+ EXPECT_EQ(mainLabel.sect, 1);
+ EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT));
+ EXPECT_EQ(mainLabel.value, Hex64(0x0));
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& printfLabel = f->undefinedSymbols[0];
+ EXPECT_TRUE(printfLabel.name.equals("_printf"));
+ EXPECT_EQ(printfLabel.type, N_UNDF);
+ EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT));
+
+ SmallString<128> tmpFl;
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
+ EXPECT_FALSE(ec);
+ llvm::Error ec2 = writeBinary(*f, tmpFl);
+ EXPECT_FALSE(ec2);
+ llvm::sys::fs::remove(tmpFl);
+}
--- /dev/null
+//===- lld/unittest/MachOTests/MachONormalizedFileBinaryWriterTests.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/FileSystem.h"
+#include "gtest/gtest.h"
+#include <cassert>
+#include <memory>
+#include <system_error>
+#include <vector>
+
+using llvm::StringRef;
+using llvm::MemoryBuffer;
+using llvm::SmallString;
+using llvm::Twine;
+using llvm::ErrorOr;
+using namespace llvm::MachO;
+using namespace lld::mach_o::normalized;
+
+// Parses binary mach-o file at specified path and returns
+// ownership of buffer to mb parameter and ownership of
+// Normalized file to nf parameter.
+static void fromBinary(StringRef path, std::unique_ptr<MemoryBuffer> &mb,
+ std::unique_ptr<NormalizedFile> &nf, StringRef archStr) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = MemoryBuffer::getFile(path);
+ std::error_code ec = mbOrErr.getError();
+ EXPECT_FALSE(ec);
+ mb = std::move(mbOrErr.get());
+
+ llvm::Expected<std::unique_ptr<NormalizedFile>> r =
+ lld::mach_o::normalized::readBinary(
+ mb, lld::MachOLinkingContext::archFromName(archStr));
+ EXPECT_FALSE(!r);
+ nf.reset(r->release());
+}
+
+static Relocation
+makeReloc(unsigned addr, bool rel, bool ext, RelocationInfoType type,
+ unsigned sym) {
+ Relocation result;
+ result.offset = addr;
+ result.scattered = false;
+ result.type = type;
+ result.length = 2;
+ result.pcRel = rel;
+ result.isExtern = ext;
+ result.value = 0;
+ result.symbol = sym;
+ return result;
+}
+
+static Relocation
+makeScatReloc(unsigned addr, RelocationInfoType type, unsigned value) {
+ Relocation result;
+ result.offset = addr;
+ result.scattered = true;
+ result.type = type;
+ result.length = 2;
+ result.pcRel = false;
+ result.isExtern = true;
+ result.value = value;
+ result.symbol = 0;
+ return result;
+}
+
+static Symbol
+makeUndefSymbol(StringRef name) {
+ Symbol sym;
+ sym.name = name;
+ sym.type = N_UNDF;
+ sym.scope = N_EXT;
+ sym.sect = NO_SECT;
+ sym.desc = 0;
+ sym.value = 0;
+ return sym;
+}
+
+
+static Symbol
+makeSymbol(StringRef name, unsigned addr) {
+ Symbol sym;
+ sym.name = name;
+ sym.type = N_SECT;
+ sym.scope = N_EXT;
+ sym.sect = 1;
+ sym.desc = 0;
+ sym.value = addr;
+ return sym;
+}
+
+static Symbol
+makeThumbSymbol(StringRef name, unsigned addr) {
+ Symbol sym;
+ sym.name = name;
+ sym.type = N_SECT;
+ sym.scope = N_EXT;
+ sym.sect = 1;
+ sym.desc = N_ARM_THUMB_DEF;
+ sym.value = addr;
+ return sym;
+}
+
+TEST(BinaryWriterTest, obj_relocs_x86_64) {
+ SmallString<128> tmpFl;
+ {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_x86_64;
+ f.fileType = MH_OBJECT;
+ f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.os = lld::MachOLinkingContext::OS::macOSX;
+ f.sections.resize(1);
+ Section& text = f.sections.front();
+ text.segmentName = "__TEXT";
+ text.sectionName = "__text";
+ text.type = S_REGULAR;
+ text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS);
+ text.alignment = 16;
+ text.address = 0;
+ const uint8_t textBytes[] = {
+ 0xe8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00,
+ 0x00, 0x00, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0xc6, 0x05, 0xff, 0xff, 0xff, 0xff, 0x12, 0xc7,
+ 0x05, 0xfc, 0xff, 0xff, 0xff, 0x78, 0x56, 0x34,
+ 0x12, 0x48, 0x8b, 0x3d, 0x00, 0x00, 0x00, 0x00 };
+
+ text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
+ text.relocations.push_back(makeReloc(0x01, false, true, X86_64_RELOC_BRANCH, 1));
+ text.relocations.push_back(makeReloc(0x08, false, true, X86_64_RELOC_GOT_LOAD, 1));
+ text.relocations.push_back(makeReloc(0x0E, false, true, X86_64_RELOC_GOT, 1));
+ text.relocations.push_back(makeReloc(0x14, false, true, X86_64_RELOC_SIGNED, 1));
+ text.relocations.push_back(makeReloc(0x1A, false, true, X86_64_RELOC_SIGNED_1, 1));
+ text.relocations.push_back(makeReloc(0x21, false, true, X86_64_RELOC_SIGNED_4, 1));
+ text.relocations.push_back(makeReloc(0x2C, false, true, X86_64_RELOC_TLV, 2));
+
+ f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
+ f.undefinedSymbols.push_back(makeUndefSymbol("_tbar"));
+
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
+ EXPECT_FALSE(ec);
+ llvm::Error ec2 = writeBinary(f, tmpFl);
+ EXPECT_FALSE(ec2);
+ }
+
+ std::unique_ptr<MemoryBuffer> bufferOwner;
+ std::unique_ptr<NormalizedFile> f2;
+ fromBinary(tmpFl, bufferOwner, f2, "x86_64");
+
+ EXPECT_EQ(lld::MachOLinkingContext::arch_x86_64, f2->arch);
+ EXPECT_EQ(MH_OBJECT, f2->fileType);
+ EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
+
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_TRUE(f2->globalSymbols.empty());
+ EXPECT_EQ(2UL, f2->undefinedSymbols.size());
+ const Symbol& barUndef = f2->undefinedSymbols[0];
+ EXPECT_TRUE(barUndef.name.equals("_bar"));
+ EXPECT_EQ(N_UNDF, barUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
+ const Symbol& tbarUndef = f2->undefinedSymbols[1];
+ EXPECT_TRUE(tbarUndef.name.equals("_tbar"));
+ EXPECT_EQ(N_UNDF, tbarUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope);
+
+ EXPECT_EQ(1UL, f2->sections.size());
+ const Section& text = f2->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(S_REGULAR, text.type);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 16U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(48UL, text.content.size());
+ const Relocation& call = text.relocations[0];
+ EXPECT_EQ(call.offset, Hex32(0x1));
+ EXPECT_EQ(call.type, X86_64_RELOC_BRANCH);
+ EXPECT_EQ(call.length, 2);
+ EXPECT_EQ(call.isExtern, true);
+ EXPECT_EQ(call.symbol, 1U);
+ const Relocation& gotLoad = text.relocations[1];
+ EXPECT_EQ(gotLoad.offset, Hex32(0x8));
+ EXPECT_EQ(gotLoad.type, X86_64_RELOC_GOT_LOAD);
+ EXPECT_EQ(gotLoad.length, 2);
+ EXPECT_EQ(gotLoad.isExtern, true);
+ EXPECT_EQ(gotLoad.symbol, 1U);
+ const Relocation& gotUse = text.relocations[2];
+ EXPECT_EQ(gotUse.offset, Hex32(0xE));
+ EXPECT_EQ(gotUse.type, X86_64_RELOC_GOT);
+ EXPECT_EQ(gotUse.length, 2);
+ EXPECT_EQ(gotUse.isExtern, true);
+ EXPECT_EQ(gotUse.symbol, 1U);
+ const Relocation& signed0 = text.relocations[3];
+ EXPECT_EQ(signed0.offset, Hex32(0x14));
+ EXPECT_EQ(signed0.type, X86_64_RELOC_SIGNED);
+ EXPECT_EQ(signed0.length, 2);
+ EXPECT_EQ(signed0.isExtern, true);
+ EXPECT_EQ(signed0.symbol, 1U);
+ const Relocation& signed1 = text.relocations[4];
+ EXPECT_EQ(signed1.offset, Hex32(0x1A));
+ EXPECT_EQ(signed1.type, X86_64_RELOC_SIGNED_1);
+ EXPECT_EQ(signed1.length, 2);
+ EXPECT_EQ(signed1.isExtern, true);
+ EXPECT_EQ(signed1.symbol, 1U);
+ const Relocation& signed4 = text.relocations[5];
+ EXPECT_EQ(signed4.offset, Hex32(0x21));
+ EXPECT_EQ(signed4.type, X86_64_RELOC_SIGNED_4);
+ EXPECT_EQ(signed4.length, 2);
+ EXPECT_EQ(signed4.isExtern, true);
+ EXPECT_EQ(signed4.symbol, 1U);
+
+ bufferOwner.reset(nullptr);
+ std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
+ EXPECT_FALSE(ec);
+}
+
+
+
+TEST(BinaryWriterTest, obj_relocs_x86) {
+ SmallString<128> tmpFl;
+ {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_x86;
+ f.fileType = MH_OBJECT;
+ f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.os = lld::MachOLinkingContext::OS::macOSX;
+ f.sections.resize(1);
+ Section& text = f.sections.front();
+ text.segmentName = "__TEXT";
+ text.sectionName = "__text";
+ text.type = S_REGULAR;
+ text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS);
+ text.alignment = 16;
+ text.address = 0;
+ const uint8_t textBytes[] = {
+ 0xe8, 0xfb, 0xff, 0xff, 0xff, 0xa1, 0x00, 0x00,
+ 0x00, 0x00, 0x8b, 0xb0, 0xfb, 0xff, 0xff, 0xff,
+ 0x8b, 0x80, 0x11, 0x00, 0x00, 0x00 };
+
+ text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
+ text.relocations.push_back(makeReloc(0x01, true, true, GENERIC_RELOC_VANILLA, 0));
+ text.relocations.push_back(makeReloc(0x06, false, true, GENERIC_RELOC_VANILLA, 0));
+ text.relocations.push_back(makeScatReloc(0x0c, GENERIC_RELOC_LOCAL_SECTDIFF, 0));
+ text.relocations.push_back(makeScatReloc(0x0, GENERIC_RELOC_PAIR, 5));
+ text.relocations.push_back(makeReloc(0x12, true, true, GENERIC_RELOC_TLV, 1));
+
+ f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
+ f.undefinedSymbols.push_back(makeUndefSymbol("_tbar"));
+
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
+ EXPECT_FALSE(ec);
+ llvm::Error ec2 = writeBinary(f, tmpFl);
+ EXPECT_FALSE(ec2);
+ }
+ std::unique_ptr<MemoryBuffer> bufferOwner;
+ std::unique_ptr<NormalizedFile> f2;
+ fromBinary(tmpFl, bufferOwner, f2, "i386");
+
+ EXPECT_EQ(lld::MachOLinkingContext::arch_x86, f2->arch);
+ EXPECT_EQ(MH_OBJECT, f2->fileType);
+ EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
+
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_TRUE(f2->globalSymbols.empty());
+ EXPECT_EQ(2UL, f2->undefinedSymbols.size());
+ const Symbol& barUndef = f2->undefinedSymbols[0];
+ EXPECT_TRUE(barUndef.name.equals("_bar"));
+ EXPECT_EQ(N_UNDF, barUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
+ const Symbol& tbarUndef = f2->undefinedSymbols[1];
+ EXPECT_TRUE(tbarUndef.name.equals("_tbar"));
+ EXPECT_EQ(N_UNDF, tbarUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope);
+
+ EXPECT_EQ(1UL, f2->sections.size());
+ const Section& text = f2->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(S_REGULAR, text.type);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 16U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(22UL, text.content.size());
+ const Relocation& call = text.relocations[0];
+ EXPECT_EQ(call.offset, Hex32(0x1));
+ EXPECT_EQ(call.scattered, false);
+ EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA);
+ EXPECT_EQ(call.pcRel, true);
+ EXPECT_EQ(call.length, 2);
+ EXPECT_EQ(call.isExtern, true);
+ EXPECT_EQ(call.symbol, 0U);
+ const Relocation& absLoad = text.relocations[1];
+ EXPECT_EQ(absLoad.offset, Hex32(0x6));
+ EXPECT_EQ(absLoad.scattered, false);
+ EXPECT_EQ(absLoad.type, GENERIC_RELOC_VANILLA);
+ EXPECT_EQ(absLoad.pcRel, false);
+ EXPECT_EQ(absLoad.length, 2);
+ EXPECT_EQ(absLoad.isExtern, true);
+ EXPECT_EQ(absLoad.symbol,0U);
+ const Relocation& pic1 = text.relocations[2];
+ EXPECT_EQ(pic1.offset, Hex32(0xc));
+ EXPECT_EQ(pic1.scattered, true);
+ EXPECT_EQ(pic1.type, GENERIC_RELOC_LOCAL_SECTDIFF);
+ EXPECT_EQ(pic1.length, 2);
+ EXPECT_EQ(pic1.value, 0U);
+ const Relocation& pic2 = text.relocations[3];
+ EXPECT_EQ(pic2.offset, Hex32(0x0));
+ EXPECT_EQ(pic1.scattered, true);
+ EXPECT_EQ(pic2.type, GENERIC_RELOC_PAIR);
+ EXPECT_EQ(pic2.length, 2);
+ EXPECT_EQ(pic2.value, 5U);
+ const Relocation& tlv = text.relocations[4];
+ EXPECT_EQ(tlv.offset, Hex32(0x12));
+ EXPECT_EQ(tlv.type, GENERIC_RELOC_TLV);
+ EXPECT_EQ(tlv.length, 2);
+ EXPECT_EQ(tlv.isExtern, true);
+ EXPECT_EQ(tlv.symbol, 1U);
+
+ // lld::errs() << "temp = " << tmpFl << "\n";
+ bufferOwner.reset(nullptr);
+ std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
+ EXPECT_FALSE(ec);
+}
+
+
+
+TEST(BinaryWriterTest, obj_relocs_armv7) {
+ SmallString<128> tmpFl;
+ {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_armv7;
+ f.fileType = MH_OBJECT;
+ f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.os = lld::MachOLinkingContext::OS::macOSX;
+ f.sections.resize(1);
+ Section& text = f.sections.front();
+ text.segmentName = "__TEXT";
+ text.sectionName = "__text";
+ text.type = S_REGULAR;
+ text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS);
+ text.alignment = 4;
+ text.address = 0;
+ const uint8_t textBytes[] = {
+ 0xff, 0xf7, 0xfe, 0xef, 0x40, 0xf2, 0x05, 0x01,
+ 0xc0, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xbf };
+
+ text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
+ text.relocations.push_back(makeReloc(0x00, true, true,
+ ARM_THUMB_RELOC_BR22, 2));
+ text.relocations.push_back(makeScatReloc(0x04,
+ ARM_RELOC_HALF_SECTDIFF, 0x10));
+ text.relocations.push_back(makeScatReloc(0x00,
+ ARM_RELOC_PAIR, 0xC));
+ text.relocations.push_back(makeScatReloc(0x08,
+ ARM_RELOC_HALF_SECTDIFF, 0x10));
+ text.relocations.push_back(makeScatReloc(0x00,
+ ARM_RELOC_PAIR, 0xC));
+ text.relocations.push_back(makeReloc(0x0C, false, true,
+ ARM_RELOC_VANILLA, 2));
+
+ f.globalSymbols.push_back(makeThumbSymbol("_foo", 0x00));
+ f.globalSymbols.push_back(makeThumbSymbol("_foo2", 0x10));
+ f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
+
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
+ EXPECT_FALSE(ec);
+ llvm::Error ec2 = writeBinary(f, tmpFl);
+ EXPECT_FALSE(ec2);
+ }
+ std::unique_ptr<MemoryBuffer> bufferOwner;
+ std::unique_ptr<NormalizedFile> f2;
+ fromBinary(tmpFl, bufferOwner, f2, "armv7");
+
+ EXPECT_EQ(lld::MachOLinkingContext::arch_armv7, f2->arch);
+ EXPECT_EQ(MH_OBJECT, f2->fileType);
+ EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
+
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_EQ(2UL, f2->globalSymbols.size());
+ const Symbol& fooDef = f2->globalSymbols[0];
+ EXPECT_TRUE(fooDef.name.equals("_foo"));
+ EXPECT_EQ(N_SECT, fooDef.type);
+ EXPECT_EQ(1, fooDef.sect);
+ EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope);
+ const Symbol& foo2Def = f2->globalSymbols[1];
+ EXPECT_TRUE(foo2Def.name.equals("_foo2"));
+ EXPECT_EQ(N_SECT, foo2Def.type);
+ EXPECT_EQ(1, foo2Def.sect);
+ EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope);
+
+ EXPECT_EQ(1UL, f2->undefinedSymbols.size());
+ const Symbol& barUndef = f2->undefinedSymbols[0];
+ EXPECT_TRUE(barUndef.name.equals("_bar"));
+ EXPECT_EQ(N_UNDF, barUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
+
+ EXPECT_EQ(1UL, f2->sections.size());
+ const Section& text = f2->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(S_REGULAR, text.type);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 4U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(18UL, text.content.size());
+ const Relocation& blx = text.relocations[0];
+ EXPECT_EQ(blx.offset, Hex32(0x0));
+ EXPECT_EQ(blx.scattered, false);
+ EXPECT_EQ(blx.type, ARM_THUMB_RELOC_BR22);
+ EXPECT_EQ(blx.pcRel, true);
+ EXPECT_EQ(blx.length, 2);
+ EXPECT_EQ(blx.isExtern, true);
+ EXPECT_EQ(blx.symbol, 2U);
+ const Relocation& movw1 = text.relocations[1];
+ EXPECT_EQ(movw1.offset, Hex32(0x4));
+ EXPECT_EQ(movw1.scattered, true);
+ EXPECT_EQ(movw1.type, ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(movw1.length, 2);
+ EXPECT_EQ(movw1.value, 0x10U);
+ const Relocation& movw2 = text.relocations[2];
+ EXPECT_EQ(movw2.offset, Hex32(0x0));
+ EXPECT_EQ(movw2.scattered, true);
+ EXPECT_EQ(movw2.type, ARM_RELOC_PAIR);
+ EXPECT_EQ(movw2.length, 2);
+ EXPECT_EQ(movw2.value, Hex32(0xC));
+ const Relocation& movt1 = text.relocations[3];
+ EXPECT_EQ(movt1.offset, Hex32(0x8));
+ EXPECT_EQ(movt1.scattered, true);
+ EXPECT_EQ(movt1.type, ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(movt1.length, 2);
+ EXPECT_EQ(movt1.value, Hex32(0x10));
+ const Relocation& movt2 = text.relocations[4];
+ EXPECT_EQ(movt2.offset, Hex32(0x0));
+ EXPECT_EQ(movt2.scattered, true);
+ EXPECT_EQ(movt2.type, ARM_RELOC_PAIR);
+ EXPECT_EQ(movt2.length, 2);
+ EXPECT_EQ(movt2.value, Hex32(0xC));
+ const Relocation& absPointer = text.relocations[5];
+ EXPECT_EQ(absPointer.offset, Hex32(0xC));
+ EXPECT_EQ(absPointer.type, ARM_RELOC_VANILLA);
+ EXPECT_EQ(absPointer.length, 2);
+ EXPECT_EQ(absPointer.isExtern, true);
+ EXPECT_EQ(absPointer.symbol, 2U);
+
+ // lld::errs() << "temp = " << tmpFl << "\n";
+ bufferOwner.reset(nullptr);
+ std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
+ EXPECT_FALSE(ec);
+}
+
+
+
+TEST(BinaryWriterTest, obj_relocs_ppc) {
+ SmallString<128> tmpFl;
+ {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_ppc;
+ f.fileType = MH_OBJECT;
+ f.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.os = lld::MachOLinkingContext::OS::macOSX;
+ f.sections.resize(1);
+ Section& text = f.sections.front();
+ text.segmentName = "__TEXT";
+ text.sectionName = "__text";
+ text.type = S_REGULAR;
+ text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS);
+ text.alignment = 4;
+ text.address = 0;
+ const uint8_t textBytes[] = {
+ 0x48, 0x00, 0x00, 0x01, 0x40, 0x82, 0xff, 0xfc,
+ 0x3c, 0x62, 0x00, 0x00, 0x3c, 0x62, 0x00, 0x00,
+ 0x80, 0x63, 0x00, 0x24, 0x80, 0x63, 0x00, 0x24,
+ 0x3c, 0x40, 0x00, 0x00, 0x3c, 0x60, 0x00, 0x00,
+ 0x80, 0x42, 0x00, 0x28, 0x80, 0x63, 0x00, 0x28,
+ 0x60, 0x00, 0x00, 0x00 };
+
+ text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes));
+ text.relocations.push_back(makeReloc(0x00, true, true,
+ PPC_RELOC_BR24, 2));
+ text.relocations.push_back(makeReloc(0x04, true, true,
+ PPC_RELOC_BR14, 2));
+ text.relocations.push_back(makeScatReloc(0x08,
+ PPC_RELOC_HI16_SECTDIFF, 0x28));
+ text.relocations.push_back(makeScatReloc(0x24,
+ PPC_RELOC_PAIR, 0x4));
+ text.relocations.push_back(makeScatReloc(0x0C,
+ PPC_RELOC_HA16_SECTDIFF, 0x28));
+ text.relocations.push_back(makeScatReloc(0x24,
+ PPC_RELOC_PAIR, 0x4));
+ text.relocations.push_back(makeScatReloc(0x10,
+ PPC_RELOC_LO16_SECTDIFF, 0x28));
+ text.relocations.push_back(makeScatReloc(0x00,
+ PPC_RELOC_PAIR, 0x4));
+ text.relocations.push_back(makeScatReloc(0x14,
+ PPC_RELOC_LO14_SECTDIFF, 0x28));
+ text.relocations.push_back(makeScatReloc(0x00,
+ PPC_RELOC_PAIR, 0x4));
+ text.relocations.push_back(makeReloc(0x18, false, false,
+ PPC_RELOC_HI16, 1));
+ text.relocations.push_back(makeReloc(0x28, false, false,
+ PPC_RELOC_PAIR, 0));
+ text.relocations.push_back(makeReloc(0x1C, false, false,
+ PPC_RELOC_HA16, 1));
+ text.relocations.push_back(makeReloc(0x28, false, false,
+ PPC_RELOC_PAIR, 0));
+ text.relocations.push_back(makeReloc(0x20, false, false,
+ PPC_RELOC_LO16, 1));
+ text.relocations.push_back(makeReloc(0x00, false, false,
+ PPC_RELOC_PAIR, 0));
+ text.relocations.push_back(makeReloc(0x24, false, false,
+ PPC_RELOC_LO14, 1));
+ text.relocations.push_back(makeReloc(0x00, false, false,
+ PPC_RELOC_PAIR, 0));
+
+ f.globalSymbols.push_back(makeSymbol("_foo", 0x00));
+ f.globalSymbols.push_back(makeSymbol("_foo2", 0x28));
+ f.undefinedSymbols.push_back(makeUndefSymbol("_bar"));
+
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl);
+ EXPECT_FALSE(ec);
+ llvm::Error ec2 = writeBinary(f, tmpFl);
+ EXPECT_FALSE(ec2);
+ }
+ std::unique_ptr<MemoryBuffer> bufferOwner;
+ std::unique_ptr<NormalizedFile> f2;
+ fromBinary(tmpFl, bufferOwner, f2, "ppc");
+
+ EXPECT_EQ(lld::MachOLinkingContext::arch_ppc, f2->arch);
+ EXPECT_EQ(MH_OBJECT, f2->fileType);
+ EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags);
+
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_EQ(2UL, f2->globalSymbols.size());
+ const Symbol& fooDef = f2->globalSymbols[0];
+ EXPECT_TRUE(fooDef.name.equals("_foo"));
+ EXPECT_EQ(N_SECT, fooDef.type);
+ EXPECT_EQ(1, fooDef.sect);
+ EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope);
+ const Symbol& foo2Def = f2->globalSymbols[1];
+ EXPECT_TRUE(foo2Def.name.equals("_foo2"));
+ EXPECT_EQ(N_SECT, foo2Def.type);
+ EXPECT_EQ(1, foo2Def.sect);
+ EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope);
+
+ EXPECT_EQ(1UL, f2->undefinedSymbols.size());
+ const Symbol& barUndef = f2->undefinedSymbols[0];
+ EXPECT_TRUE(barUndef.name.equals("_bar"));
+ EXPECT_EQ(N_UNDF, barUndef.type);
+ EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope);
+
+ EXPECT_EQ(1UL, f2->sections.size());
+ const Section& text = f2->sections[0];
+ EXPECT_TRUE(text.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(text.sectionName.equals("__text"));
+ EXPECT_EQ(S_REGULAR, text.type);
+ EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS
+ | S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)text.alignment, 4U);
+ EXPECT_EQ(text.address, Hex64(0x0));
+ EXPECT_EQ(44UL, text.content.size());
+ const Relocation& br24 = text.relocations[0];
+ EXPECT_EQ(br24.offset, Hex32(0x0));
+ EXPECT_EQ(br24.scattered, false);
+ EXPECT_EQ(br24.type, PPC_RELOC_BR24);
+ EXPECT_EQ(br24.pcRel, true);
+ EXPECT_EQ(br24.length, 2);
+ EXPECT_EQ(br24.isExtern, true);
+ EXPECT_EQ(br24.symbol, 2U);
+ const Relocation& br14 = text.relocations[1];
+ EXPECT_EQ(br14.offset, Hex32(0x4));
+ EXPECT_EQ(br14.scattered, false);
+ EXPECT_EQ(br14.type, PPC_RELOC_BR14);
+ EXPECT_EQ(br14.pcRel, true);
+ EXPECT_EQ(br14.length, 2);
+ EXPECT_EQ(br14.isExtern, true);
+ EXPECT_EQ(br14.symbol, 2U);
+ const Relocation& pichi1 = text.relocations[2];
+ EXPECT_EQ(pichi1.offset, Hex32(0x8));
+ EXPECT_EQ(pichi1.scattered, true);
+ EXPECT_EQ(pichi1.type, PPC_RELOC_HI16_SECTDIFF);
+ EXPECT_EQ(pichi1.length, 2);
+ EXPECT_EQ(pichi1.value, 0x28U);
+ const Relocation& pichi2 = text.relocations[3];
+ EXPECT_EQ(pichi2.offset, Hex32(0x24));
+ EXPECT_EQ(pichi2.scattered, true);
+ EXPECT_EQ(pichi2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(pichi2.length, 2);
+ EXPECT_EQ(pichi2.value, 0x4U);
+ const Relocation& picha1 = text.relocations[4];
+ EXPECT_EQ(picha1.offset, Hex32(0xC));
+ EXPECT_EQ(picha1.scattered, true);
+ EXPECT_EQ(picha1.type, PPC_RELOC_HA16_SECTDIFF);
+ EXPECT_EQ(picha1.length, 2);
+ EXPECT_EQ(picha1.value, 0x28U);
+ const Relocation& picha2 = text.relocations[5];
+ EXPECT_EQ(picha2.offset, Hex32(0x24));
+ EXPECT_EQ(picha2.scattered, true);
+ EXPECT_EQ(picha2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(picha2.length, 2);
+ EXPECT_EQ(picha2.value, 0x4U);
+ const Relocation& piclo1 = text.relocations[6];
+ EXPECT_EQ(piclo1.offset, Hex32(0x10));
+ EXPECT_EQ(piclo1.scattered, true);
+ EXPECT_EQ(piclo1.type, PPC_RELOC_LO16_SECTDIFF);
+ EXPECT_EQ(piclo1.length, 2);
+ EXPECT_EQ(piclo1.value, 0x28U);
+ const Relocation& piclo2 = text.relocations[7];
+ EXPECT_EQ(piclo2.offset, Hex32(0x0));
+ EXPECT_EQ(piclo2.scattered, true);
+ EXPECT_EQ(piclo2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(piclo2.length, 2);
+ EXPECT_EQ(piclo2.value, 0x4U);
+ const Relocation& picloa1 = text.relocations[8];
+ EXPECT_EQ(picloa1.offset, Hex32(0x14));
+ EXPECT_EQ(picloa1.scattered, true);
+ EXPECT_EQ(picloa1.type, PPC_RELOC_LO14_SECTDIFF);
+ EXPECT_EQ(picloa1.length, 2);
+ EXPECT_EQ(picloa1.value, 0x28U);
+ const Relocation& picloa2 = text.relocations[9];
+ EXPECT_EQ(picloa2.offset, Hex32(0x0));
+ EXPECT_EQ(picloa2.scattered, true);
+ EXPECT_EQ(picloa2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(picloa2.length, 2);
+ EXPECT_EQ(picloa2.value, 0x4U);
+ const Relocation& abshi1 = text.relocations[10];
+ EXPECT_EQ(abshi1.offset, Hex32(0x18));
+ EXPECT_EQ(abshi1.scattered, false);
+ EXPECT_EQ(abshi1.type, PPC_RELOC_HI16);
+ EXPECT_EQ(abshi1.length, 2);
+ EXPECT_EQ(abshi1.symbol, 1U);
+ const Relocation& abshi2 = text.relocations[11];
+ EXPECT_EQ(abshi2.offset, Hex32(0x28));
+ EXPECT_EQ(abshi2.scattered, false);
+ EXPECT_EQ(abshi2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(abshi2.length, 2);
+ EXPECT_EQ(abshi2.symbol, 0U);
+ const Relocation& absha1 = text.relocations[12];
+ EXPECT_EQ(absha1.offset, Hex32(0x1C));
+ EXPECT_EQ(absha1.scattered, false);
+ EXPECT_EQ(absha1.type, PPC_RELOC_HA16);
+ EXPECT_EQ(absha1.length, 2);
+ EXPECT_EQ(absha1.symbol, 1U);
+ const Relocation& absha2 = text.relocations[13];
+ EXPECT_EQ(absha2.offset, Hex32(0x28));
+ EXPECT_EQ(absha2.scattered, false);
+ EXPECT_EQ(absha2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(absha2.length, 2);
+ EXPECT_EQ(absha2.symbol, 0U);
+ const Relocation& abslo1 = text.relocations[14];
+ EXPECT_EQ(abslo1.offset, Hex32(0x20));
+ EXPECT_EQ(abslo1.scattered, false);
+ EXPECT_EQ(abslo1.type, PPC_RELOC_LO16);
+ EXPECT_EQ(abslo1.length, 2);
+ EXPECT_EQ(abslo1.symbol, 1U);
+ const Relocation& abslo2 = text.relocations[15];
+ EXPECT_EQ(abslo2.offset, Hex32(0x00));
+ EXPECT_EQ(abslo2.scattered, false);
+ EXPECT_EQ(abslo2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(abslo2.length, 2);
+ EXPECT_EQ(abslo2.symbol, 0U);
+ const Relocation& absloa1 = text.relocations[16];
+ EXPECT_EQ(absloa1.offset, Hex32(0x24));
+ EXPECT_EQ(absloa1.scattered, false);
+ EXPECT_EQ(absloa1.type, PPC_RELOC_LO14);
+ EXPECT_EQ(absloa1.length, 2);
+ EXPECT_EQ(absloa1.symbol, 1U);
+ const Relocation& absloa2 = text.relocations[17];
+ EXPECT_EQ(absloa2.offset, Hex32(0x00));
+ EXPECT_EQ(absloa2.scattered, false);
+ EXPECT_EQ(absloa2.type, PPC_RELOC_PAIR);
+ EXPECT_EQ(absloa2.length, 2);
+ EXPECT_EQ(absloa2.symbol, 0U);
+
+ bufferOwner.reset(nullptr);
+ std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
+ EXPECT_FALSE(ec);
+}
--- /dev/null
+//===- lld/unittest/MachOTests/MachONormalizedFileToAtomsTests.cpp --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
+#include "lld/Core/Atom.h"
+#include "lld/Core/DefinedAtom.h"
+#include "lld/Core/File.h"
+#include "lld/Core/UndefinedAtom.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <memory>
+
+using namespace lld::mach_o::normalized;
+using namespace llvm::MachO;
+
+TEST(ToAtomsTest, empty_obj_x86_64) {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_x86_64;
+ llvm::Expected<std::unique_ptr<const lld::File>> atom_f =
+ normalizedToAtoms(f, "", false);
+ EXPECT_FALSE(!atom_f);
+ EXPECT_EQ(0U, (*atom_f)->defined().size());
+}
+
+TEST(ToAtomsTest, basic_obj_x86_64) {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_x86_64;
+ Section textSection;
+ static const uint8_t contentBytes[] = { 0x90, 0xC3, 0xC3, 0xC4 };
+ const unsigned contentSize = sizeof(contentBytes) / sizeof(contentBytes[0]);
+ textSection.content = llvm::makeArrayRef(contentBytes, contentSize);
+ f.sections.push_back(textSection);
+ Symbol fooSymbol;
+ fooSymbol.name = "_foo";
+ fooSymbol.type = N_SECT;
+ fooSymbol.scope = N_EXT;
+ fooSymbol.sect = 1;
+ fooSymbol.value = 0;
+ f.globalSymbols.push_back(fooSymbol);
+ Symbol barSymbol;
+ barSymbol.name = "_bar";
+ barSymbol.type = N_SECT;
+ barSymbol.scope = N_EXT;
+ barSymbol.sect = 1;
+ barSymbol.value = 2;
+ f.globalSymbols.push_back(barSymbol);
+ Symbol undefSym;
+ undefSym.name = "_undef";
+ undefSym.type = N_UNDF;
+ f.undefinedSymbols.push_back(undefSym);
+ Symbol bazSymbol;
+ bazSymbol.name = "_baz";
+ bazSymbol.type = N_SECT;
+ bazSymbol.scope = N_EXT | N_PEXT;
+ bazSymbol.sect = 1;
+ bazSymbol.value = 3;
+ f.localSymbols.push_back(bazSymbol);
+
+ llvm::Expected<std::unique_ptr<const lld::File>> atom_f =
+ normalizedToAtoms(f, "", false);
+ EXPECT_FALSE(!atom_f);
+ const lld::File &file = **atom_f;
+ EXPECT_EQ(3U, file.defined().size());
+ auto it = file.defined().begin();
+ const lld::DefinedAtom *atom1 = *it;
+ ++it;
+ const lld::DefinedAtom *atom2 = *it;
+ ++it;
+ const lld::DefinedAtom *atom3 = *it;
+ const lld::UndefinedAtom *atom4 = *file.undefined().begin();
+ EXPECT_TRUE(atom1->name().equals("_foo"));
+ EXPECT_EQ(2U, atom1->rawContent().size());
+ EXPECT_EQ(0x90, atom1->rawContent()[0]);
+ EXPECT_EQ(0xC3, atom1->rawContent()[1]);
+ EXPECT_EQ(lld::Atom::scopeGlobal, atom1->scope());
+
+ EXPECT_TRUE(atom2->name().equals("_bar"));
+ EXPECT_EQ(1U, atom2->rawContent().size());
+ EXPECT_EQ(0xC3, atom2->rawContent()[0]);
+ EXPECT_EQ(lld::Atom::scopeGlobal, atom2->scope());
+
+ EXPECT_TRUE(atom3->name().equals("_baz"));
+ EXPECT_EQ(1U, atom3->rawContent().size());
+ EXPECT_EQ(0xC4, atom3->rawContent()[0]);
+ EXPECT_EQ(lld::Atom::scopeLinkageUnit, atom3->scope());
+
+ EXPECT_TRUE(atom4->name().equals("_undef"));
+ EXPECT_EQ(lld::Atom::definitionUndefined, atom4->definition());
+}
+
+TEST(ToAtomsTest, reservedUnitLength) {
+ static const uint8_t debugInfoWithReservedLengthContent[12] = {
+ 0xf0, 0xff, 0xff, 0xff // Reserved length value
+ };
+ static const uint8_t debugInfoWithValidBigLengthContent[12] = {
+ 0xef, 0xff, 0xff, 0xff, // The maximum valid length value for DWARF32
+ 0x00, 0x00 // Wrong version
+ };
+ static const uint8_t dummyContent[] = {0x00};
+
+ NormalizedFile fReservedLength, fValidBigLength;
+ fReservedLength.arch = lld::MachOLinkingContext::arch_x86;
+ fValidBigLength.arch = lld::MachOLinkingContext::arch_x86;
+ Section section;
+ section.segmentName = "__DWARF";
+ section.sectionName = "__debug_info";
+ section.content = llvm::makeArrayRef(debugInfoWithReservedLengthContent);
+ fReservedLength.sections.push_back(section);
+ section.content = llvm::makeArrayRef(debugInfoWithValidBigLengthContent);
+ fValidBigLength.sections.push_back(section);
+ section.sectionName = "__debug_abbrev";
+ section.content = llvm::makeArrayRef(dummyContent);
+ fReservedLength.sections.push_back(section);
+ fValidBigLength.sections.push_back(section);
+ section.sectionName = "__debug_str";
+ fReservedLength.sections.push_back(section);
+ fValidBigLength.sections.push_back(section);
+
+ auto resultReservedLength = normalizedToAtoms(fReservedLength, "foo", false);
+ auto resultValidBigLength = normalizedToAtoms(fValidBigLength, "foo", false);
+
+ // Both cases should return errors, but different.
+ ASSERT_FALSE(resultReservedLength);
+ ASSERT_FALSE(resultValidBigLength);
+
+ EXPECT_STREQ("Malformed DWARF in foo",
+ toString(resultReservedLength.takeError()).c_str());
+ EXPECT_STREQ("Unsupported DWARF version in foo",
+ toString(resultValidBigLength.takeError()).c_str());
+}
--- /dev/null
+//===- lld/unittest/MachOTests/MachONormalizedFileYAMLTests.cpp -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <system_error>
+
+using llvm::StringRef;
+using llvm::MemoryBuffer;
+using lld::mach_o::normalized::NormalizedFile;
+using lld::mach_o::normalized::Symbol;
+using lld::mach_o::normalized::Section;
+using lld::mach_o::normalized::Relocation;
+
+static std::unique_ptr<NormalizedFile> fromYAML(StringRef str) {
+ std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(str));
+ llvm::Expected<std::unique_ptr<NormalizedFile>> r
+ = lld::mach_o::normalized::readYaml(mb);
+ EXPECT_FALSE(!r);
+ return std::move(*r);
+}
+
+static void toYAML(const NormalizedFile &f, std::string &out) {
+ llvm::raw_string_ostream ostr(out);
+ std::error_code ec = lld::mach_o::normalized::writeYaml(f, ostr);
+ EXPECT_TRUE(!ec);
+}
+
+// ppc is no longer supported, but it is here to test endianness handling.
+TEST(ObjectFileYAML, empty_ppc) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: ppc\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, empty_x86_64) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86_64\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, empty_x86) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, empty_armv6) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: armv6\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, empty_armv7) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: armv7\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, empty_armv7s) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: armv7s\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7s);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+}
+
+TEST(ObjectFileYAML, roundTrip) {
+ std::string intermediate;
+ {
+ NormalizedFile f;
+ f.arch = lld::MachOLinkingContext::arch_x86_64;
+ f.fileType = llvm::MachO::MH_OBJECT;
+ f.flags = (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.os = lld::MachOLinkingContext::OS::macOSX;
+ toYAML(f, intermediate);
+ }
+ {
+ std::unique_ptr<NormalizedFile> f2 = fromYAML(intermediate);
+ EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ((int)(f2->fileType), llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f2->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_TRUE(f2->sections.empty());
+ EXPECT_TRUE(f2->localSymbols.empty());
+ EXPECT_TRUE(f2->globalSymbols.empty());
+ EXPECT_TRUE(f2->undefinedSymbols.empty());
+ }
+}
+
+TEST(ObjectFileYAML, oneSymbol) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86_64\n"
+ "file-type: MH_OBJECT\n"
+ "global-symbols:\n"
+ " - name: _main\n"
+ " type: N_SECT\n"
+ " scope: [ N_EXT ]\n"
+ " sect: 1\n"
+ " desc: [ ]\n"
+ " value: 0x100\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_TRUE(f->sections.empty());
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& sym = f->globalSymbols[0];
+ EXPECT_TRUE(sym.name.equals("_main"));
+ EXPECT_EQ((int)(sym.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym.scope), llvm::MachO::N_EXT);
+ EXPECT_EQ(sym.sect, 1);
+ EXPECT_EQ((int)(sym.desc), 0);
+ EXPECT_EQ((uint64_t)sym.value, 0x100ULL);
+}
+
+TEST(ObjectFileYAML, oneSection) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86_64\n"
+ "file-type: MH_OBJECT\n"
+ "sections:\n"
+ " - segment: __TEXT\n"
+ " section: __text\n"
+ " type: S_REGULAR\n"
+ " attributes: [ S_ATTR_PURE_INSTRUCTIONS ]\n"
+ " alignment: 2\n"
+ " address: 0x12345678\n"
+ " content: [ 0x90, 0x90 ]\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_TRUE(f->localSymbols.empty());
+ EXPECT_TRUE(f->globalSymbols.empty());
+ EXPECT_TRUE(f->undefinedSymbols.empty());
+ EXPECT_EQ(f->sections.size(), 1UL);
+ const Section& sect = f->sections[0];
+ EXPECT_TRUE(sect.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect.sectionName.equals("__text"));
+ EXPECT_EQ((uint32_t)(sect.type), (uint32_t)(llvm::MachO::S_REGULAR));
+ EXPECT_EQ((uint32_t)(sect.attributes),
+ (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)sect.alignment, 2U);
+ EXPECT_EQ((uint64_t)sect.address, 0x12345678ULL);
+ EXPECT_EQ(sect.content.size(), 2UL);
+ EXPECT_EQ((int)(sect.content[0]), 0x90);
+ EXPECT_EQ((int)(sect.content[1]), 0x90);
+}
+
+TEST(ObjectFileYAML, hello_x86_64) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86_64\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "sections:\n"
+ " - segment: __TEXT\n"
+ " section: __text\n"
+ " type: S_REGULAR\n"
+ " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
+ " alignment: 1\n"
+ " address: 0x0000\n"
+ " content: [ 0x55, 0x48, 0x89, 0xe5, 0x48, 0x8d, 0x3d, 0x00,\n"
+ " 0x00, 0x00, 0x00, 0x30, 0xc0, 0xe8, 0x00, 0x00,\n"
+ " 0x00, 0x00, 0x31, 0xc0, 0x5d, 0xc3 ]\n"
+ " relocations:\n"
+ " - offset: 0x0e\n"
+ " type: X86_64_RELOC_BRANCH\n"
+ " length: 2\n"
+ " pc-rel: true\n"
+ " extern: true\n"
+ " symbol: 2\n"
+ " - offset: 0x07\n"
+ " type: X86_64_RELOC_SIGNED\n"
+ " length: 2\n"
+ " pc-rel: true\n"
+ " extern: true\n"
+ " symbol: 1\n"
+ " - segment: __TEXT\n"
+ " section: __cstring\n"
+ " type: S_CSTRING_LITERALS\n"
+ " attributes: [ ]\n"
+ " alignment: 1\n"
+ " address: 0x0016\n"
+ " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
+ "global-symbols:\n"
+ " - name: _main\n"
+ " type: N_SECT\n"
+ " scope: [ N_EXT ]\n"
+ " sect: 1\n"
+ " value: 0x0\n"
+ "local-symbols:\n"
+ " - name: L_.str\n"
+ " type: N_SECT\n"
+ " scope: [ ]\n"
+ " sect: 2\n"
+ " value: 0x16\n"
+ "undefined-symbols:\n"
+ " - name: _printf\n"
+ " type: N_UNDF\n"
+ " value: 0x0\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+
+ const Section& sect1 = f->sections[0];
+ EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect1.sectionName.equals("__text"));
+ EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
+ EXPECT_EQ((uint32_t)(sect1.attributes),
+ (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
+ | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)sect1.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
+ EXPECT_EQ(sect1.content.size(), 22UL);
+ EXPECT_EQ((int)(sect1.content[0]), 0x55);
+ EXPECT_EQ((int)(sect1.content[1]), 0x48);
+ EXPECT_EQ(sect1.relocations.size(), 2UL);
+ const Relocation& reloc1 = sect1.relocations[0];
+ EXPECT_EQ(reloc1.offset, 0x0eU);
+ EXPECT_FALSE(reloc1.scattered);
+ EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::X86_64_RELOC_BRANCH);
+ EXPECT_EQ(reloc1.length, 2);
+ EXPECT_TRUE(reloc1.pcRel);
+ EXPECT_TRUE(reloc1.isExtern);
+ EXPECT_EQ(reloc1.symbol, 2U);
+ EXPECT_EQ((int)(reloc1.value), 0);
+ const Relocation& reloc2 = sect1.relocations[1];
+ EXPECT_EQ(reloc2.offset, 0x07U);
+ EXPECT_FALSE(reloc2.scattered);
+ EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::X86_64_RELOC_SIGNED);
+ EXPECT_EQ(reloc2.length, 2);
+ EXPECT_TRUE(reloc2.pcRel);
+ EXPECT_TRUE(reloc2.isExtern);
+ EXPECT_EQ(reloc2.symbol, 1U);
+ EXPECT_EQ((int)(reloc2.value), 0);
+
+ const Section& sect2 = f->sections[1];
+ EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
+ EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
+ EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
+ EXPECT_EQ((uint16_t)sect2.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect2.address, 0x016ULL);
+ EXPECT_EQ(sect2.content.size(), 7UL);
+ EXPECT_EQ((int)(sect2.content[0]), 0x68);
+ EXPECT_EQ((int)(sect2.content[1]), 0x65);
+ EXPECT_EQ((int)(sect2.content[2]), 0x6c);
+
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& sym1 = f->globalSymbols[0];
+ EXPECT_TRUE(sym1.name.equals("_main"));
+ EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
+ EXPECT_EQ(sym1.sect, 1);
+ EXPECT_EQ((int)(sym1.desc), 0);
+ EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
+ EXPECT_EQ(f->localSymbols.size(), 1UL);
+ const Symbol& sym2 = f->localSymbols[0];
+ EXPECT_TRUE(sym2.name.equals("L_.str"));
+ EXPECT_EQ((int)(sym2.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym2.scope), 0);
+ EXPECT_EQ(sym2.sect, 2);
+ EXPECT_EQ((int)(sym2.desc), 0);
+ EXPECT_EQ((uint64_t)sym2.value, 0x16ULL);
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& sym3 = f->undefinedSymbols[0];
+ EXPECT_TRUE(sym3.name.equals("_printf"));
+ EXPECT_EQ((int)(sym3.type), llvm::MachO::N_UNDF);
+ EXPECT_EQ((int)(sym3.scope), 0);
+ EXPECT_EQ(sym3.sect, 0);
+ EXPECT_EQ((int)(sym3.desc), 0);
+ EXPECT_EQ((uint64_t)sym3.value, 0x0ULL);
+}
+
+TEST(ObjectFileYAML, hello_x86) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: x86\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "sections:\n"
+ " - segment: __TEXT\n"
+ " section: __text\n"
+ " type: S_REGULAR\n"
+ " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
+ " alignment: 1\n"
+ " address: 0x0000\n"
+ " content: [ 0x55, 0x89, 0xe5, 0x83, 0xec, 0x08, 0xe8, 0x00,\n"
+ " 0x00, 0x00, 0x00, 0x58, 0x8d, 0x80, 0x16, 0x00,\n"
+ " 0x00, 0x00, 0x89, 0x04, 0x24, 0xe8, 0xe6, 0xff,\n"
+ " 0xff, 0xff, 0x31, 0xc0, 0x83, 0xc4, 0x08, 0x5d,\n"
+ " 0xc3 ]\n"
+ " relocations:\n"
+ " - offset: 0x16\n"
+ " type: GENERIC_RELOC_VANILLA\n"
+ " length: 2\n"
+ " pc-rel: true\n"
+ " extern: true\n"
+ " symbol: 1\n"
+ " - offset: 0x0e\n"
+ " scattered: true\n"
+ " type: GENERIC_RELOC_LOCAL_SECTDIFF\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0x21\n"
+ " - offset: 0x0\n"
+ " scattered: true\n"
+ " type: GENERIC_RELOC_PAIR\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0xb\n"
+ " - segment: __TEXT\n"
+ " section: __cstring\n"
+ " type: S_CSTRING_LITERALS\n"
+ " attributes: [ ]\n"
+ " alignment: 1\n"
+ " address: 0x0021\n"
+ " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
+ "global-symbols:\n"
+ " - name: _main\n"
+ " type: N_SECT\n"
+ " scope: [ N_EXT ]\n"
+ " sect: 1\n"
+ " value: 0x0\n"
+ "undefined-symbols:\n"
+ " - name: _printf\n"
+ " type: N_UNDF\n"
+ " value: 0x0\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+
+ const Section& sect1 = f->sections[0];
+ EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect1.sectionName.equals("__text"));
+ EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
+ EXPECT_EQ((uint32_t)(sect1.attributes),
+ (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
+ | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)sect1.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
+ EXPECT_EQ(sect1.content.size(), 33UL);
+ EXPECT_EQ((int)(sect1.content[0]), 0x55);
+ EXPECT_EQ((int)(sect1.content[1]), 0x89);
+ EXPECT_EQ(sect1.relocations.size(), 3UL);
+ const Relocation& reloc1 = sect1.relocations[0];
+ EXPECT_EQ(reloc1.offset, 0x16U);
+ EXPECT_FALSE(reloc1.scattered);
+ EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::GENERIC_RELOC_VANILLA);
+ EXPECT_EQ(reloc1.length, 2);
+ EXPECT_TRUE(reloc1.pcRel);
+ EXPECT_TRUE(reloc1.isExtern);
+ EXPECT_EQ(reloc1.symbol, 1U);
+ EXPECT_EQ((int)(reloc1.value), 0);
+ const Relocation& reloc2 = sect1.relocations[1];
+ EXPECT_EQ(reloc2.offset, 0x0eU);
+ EXPECT_TRUE(reloc2.scattered);
+ EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF);
+ EXPECT_EQ(reloc2.length, 2);
+ EXPECT_FALSE(reloc2.pcRel);
+ EXPECT_EQ(reloc2.symbol, 0U);
+ EXPECT_EQ((int)(reloc2.value), 0x21);
+ const Relocation& reloc3 = sect1.relocations[2];
+ EXPECT_EQ(reloc3.offset, 0U);
+ EXPECT_TRUE(reloc3.scattered);
+ EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::GENERIC_RELOC_PAIR);
+ EXPECT_EQ(reloc3.length, 2);
+ EXPECT_FALSE(reloc3.pcRel);
+ EXPECT_EQ(reloc3.symbol, 0U);
+ EXPECT_EQ((int)(reloc3.value), 0xb);
+
+ const Section& sect2 = f->sections[1];
+ EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
+ EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
+ EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
+ EXPECT_EQ((uint16_t)sect2.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect2.address, 0x021ULL);
+ EXPECT_EQ(sect2.content.size(), 7UL);
+ EXPECT_EQ((int)(sect2.content[0]), 0x68);
+ EXPECT_EQ((int)(sect2.content[1]), 0x65);
+ EXPECT_EQ((int)(sect2.content[2]), 0x6c);
+
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& sym1 = f->globalSymbols[0];
+ EXPECT_TRUE(sym1.name.equals("_main"));
+ EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
+ EXPECT_EQ(sym1.sect, 1);
+ EXPECT_EQ((int)(sym1.desc), 0);
+ EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& sym2 = f->undefinedSymbols[0];
+ EXPECT_TRUE(sym2.name.equals("_printf"));
+ EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
+ EXPECT_EQ((int)(sym2.scope), 0);
+ EXPECT_EQ(sym2.sect, 0);
+ EXPECT_EQ((int)(sym2.desc), 0);
+ EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
+}
+
+TEST(ObjectFileYAML, hello_armv6) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: armv6\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "sections:\n"
+ " - segment: __TEXT\n"
+ " section: __text\n"
+ " type: S_REGULAR\n"
+ " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
+ " alignment: 4\n"
+ " address: 0x0000\n"
+ " content: [ 0x80, 0x40, 0x2d, 0xe9, 0x10, 0x00, 0x9f, 0xe5,\n"
+ " 0x0d, 0x70, 0xa0, 0xe1, 0x00, 0x00, 0x8f, 0xe0,\n"
+ " 0xfa, 0xff, 0xff, 0xeb, 0x00, 0x00, 0xa0, 0xe3,\n"
+ " 0x80, 0x80, 0xbd, 0xe8, 0x0c, 0x00, 0x00, 0x00 ]\n"
+ " relocations:\n"
+ " - offset: 0x1c\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_SECTDIFF\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0x20\n"
+ " - offset: 0x0\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_PAIR\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0xc\n"
+ " - offset: 0x10\n"
+ " type: ARM_RELOC_BR24\n"
+ " length: 2\n"
+ " pc-rel: true\n"
+ " extern: true\n"
+ " symbol: 1\n"
+ " - segment: __TEXT\n"
+ " section: __cstring\n"
+ " type: S_CSTRING_LITERALS\n"
+ " attributes: [ ]\n"
+ " alignment: 1\n"
+ " address: 0x0020\n"
+ " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
+ "global-symbols:\n"
+ " - name: _main\n"
+ " type: N_SECT\n"
+ " scope: [ N_EXT ]\n"
+ " sect: 1\n"
+ " value: 0x0\n"
+ "undefined-symbols:\n"
+ " - name: _printf\n"
+ " type: N_UNDF\n"
+ " value: 0x0\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+
+ const Section& sect1 = f->sections[0];
+ EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect1.sectionName.equals("__text"));
+ EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
+ EXPECT_EQ((uint32_t)(sect1.attributes),
+ (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
+ | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)sect1.alignment, 4U);
+ EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
+ EXPECT_EQ(sect1.content.size(), 32UL);
+ EXPECT_EQ((int)(sect1.content[0]), 0x80);
+ EXPECT_EQ((int)(sect1.content[1]), 0x40);
+ EXPECT_EQ(sect1.relocations.size(), 3UL);
+ const Relocation& reloc1 = sect1.relocations[0];
+ EXPECT_EQ(reloc1.offset, 0x1cU);
+ EXPECT_TRUE(reloc1.scattered);
+ EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_RELOC_SECTDIFF);
+ EXPECT_EQ(reloc1.length, 2);
+ EXPECT_FALSE(reloc1.pcRel);
+ EXPECT_EQ(reloc1.symbol, 0U);
+ EXPECT_EQ((int)(reloc1.value), 0x20);
+ const Relocation& reloc2 = sect1.relocations[1];
+ EXPECT_EQ(reloc2.offset, 0x0U);
+ EXPECT_TRUE(reloc2.scattered);
+ EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_PAIR);
+ EXPECT_EQ(reloc2.length, 2);
+ EXPECT_FALSE(reloc2.pcRel);
+ EXPECT_EQ(reloc2.symbol, 0U);
+ EXPECT_EQ((int)(reloc2.value), 0xc);
+ const Relocation& reloc3 = sect1.relocations[2];
+ EXPECT_EQ(reloc3.offset, 0x10U);
+ EXPECT_FALSE(reloc3.scattered);
+ EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_BR24);
+ EXPECT_EQ(reloc3.length, 2);
+ EXPECT_TRUE(reloc3.pcRel);
+ EXPECT_TRUE(reloc3.isExtern);
+ EXPECT_EQ(reloc3.symbol, 1U);
+ EXPECT_EQ((int)(reloc3.value), 0);
+
+ const Section& sect2 = f->sections[1];
+ EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
+ EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
+ EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
+ EXPECT_EQ((uint16_t)sect2.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect2.address, 0x020ULL);
+ EXPECT_EQ(sect2.content.size(), 7UL);
+ EXPECT_EQ((int)(sect2.content[0]), 0x68);
+ EXPECT_EQ((int)(sect2.content[1]), 0x65);
+ EXPECT_EQ((int)(sect2.content[2]), 0x6c);
+
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& sym1 = f->globalSymbols[0];
+ EXPECT_TRUE(sym1.name.equals("_main"));
+ EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
+ EXPECT_EQ(sym1.sect, 1);
+ EXPECT_EQ((int)(sym1.desc), 0);
+ EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& sym2 = f->undefinedSymbols[0];
+ EXPECT_TRUE(sym2.name.equals("_printf"));
+ EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
+ EXPECT_EQ((int)(sym2.scope), 0);
+ EXPECT_EQ(sym2.sect, 0);
+ EXPECT_EQ((int)(sym2.desc), 0);
+ EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
+}
+
+TEST(ObjectFileYAML, hello_armv7) {
+ std::unique_ptr<NormalizedFile> f = fromYAML(
+ "---\n"
+ "arch: armv7\n"
+ "file-type: MH_OBJECT\n"
+ "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n"
+ "sections:\n"
+ " - segment: __TEXT\n"
+ " section: __text\n"
+ " type: S_REGULAR\n"
+ " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n"
+ " alignment: 2\n"
+ " address: 0x0000\n"
+ " content: [ 0x80, 0xb5, 0x40, 0xf2, 0x06, 0x00, 0x6f, 0x46,\n"
+ " 0xc0, 0xf2, 0x00, 0x00, 0x78, 0x44, 0xff, 0xf7,\n"
+ " 0xf8, 0xef, 0x00, 0x20, 0x80, 0xbd ]\n"
+ " relocations:\n"
+ " - offset: 0x0e\n"
+ " type: ARM_THUMB_RELOC_BR22\n"
+ " length: 2\n"
+ " pc-rel: true\n"
+ " extern: true\n"
+ " symbol: 1\n"
+ " - offset: 0x08\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_HALF_SECTDIFF\n"
+ " length: 3\n"
+ " pc-rel: false\n"
+ " value: 0x16\n"
+ " - offset: 0x06\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_PAIR\n"
+ " length: 3\n"
+ " pc-rel: false\n"
+ " value: 0xc\n"
+ " - offset: 0x02\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_HALF_SECTDIFF\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0x16\n"
+ " - offset: 0x0\n"
+ " scattered: true\n"
+ " type: ARM_RELOC_PAIR\n"
+ " length: 2\n"
+ " pc-rel: false\n"
+ " value: 0xc\n"
+ " - segment: __TEXT\n"
+ " section: __cstring\n"
+ " type: S_CSTRING_LITERALS\n"
+ " attributes: [ ]\n"
+ " alignment: 1\n"
+ " address: 0x0016\n"
+ " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n"
+ "global-symbols:\n"
+ " - name: _main\n"
+ " type: N_SECT\n"
+ " scope: [ N_EXT ]\n"
+ " sect: 1\n"
+ " desc: [ N_ARM_THUMB_DEF ]\n"
+ " value: 0x0\n"
+ "undefined-symbols:\n"
+ " - name: _printf\n"
+ " type: N_UNDF\n"
+ " value: 0x0\n"
+ "...\n");
+ EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
+ EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ(f->sections.size(), 2UL);
+
+ const Section& sect1 = f->sections[0];
+ EXPECT_TRUE(sect1.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect1.sectionName.equals("__text"));
+ EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR));
+ EXPECT_EQ((uint32_t)(sect1.attributes),
+ (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS
+ | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS));
+ EXPECT_EQ((uint16_t)sect1.alignment, 2U);
+ EXPECT_EQ((uint64_t)sect1.address, 0x0ULL);
+ EXPECT_EQ(sect1.content.size(), 22UL);
+ EXPECT_EQ((int)(sect1.content[0]), 0x80);
+ EXPECT_EQ((int)(sect1.content[1]), 0xb5);
+ EXPECT_EQ(sect1.relocations.size(), 5UL);
+ const Relocation& reloc1 = sect1.relocations[0];
+ EXPECT_EQ(reloc1.offset, 0x0eU);
+ EXPECT_FALSE(reloc1.scattered);
+ EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_THUMB_RELOC_BR22);
+ EXPECT_EQ(reloc1.length, 2);
+ EXPECT_TRUE(reloc1.pcRel);
+ EXPECT_TRUE(reloc1.isExtern);
+ EXPECT_EQ(reloc1.symbol, 1U);
+ EXPECT_EQ((int)(reloc1.value), 0);
+ const Relocation& reloc2 = sect1.relocations[1];
+ EXPECT_EQ(reloc2.offset, 0x8U);
+ EXPECT_TRUE(reloc2.scattered);
+ EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(reloc2.length, 3);
+ EXPECT_FALSE(reloc2.pcRel);
+ EXPECT_EQ(reloc2.symbol, 0U);
+ EXPECT_EQ((int)(reloc2.value), 0x16);
+ const Relocation& reloc3 = sect1.relocations[2];
+ EXPECT_EQ(reloc3.offset, 0x6U);
+ EXPECT_TRUE(reloc3.scattered);
+ EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_PAIR);
+ EXPECT_EQ(reloc3.length, 3);
+ EXPECT_FALSE(reloc3.pcRel);
+ EXPECT_EQ(reloc3.symbol, 0U);
+ EXPECT_EQ((int)(reloc3.value), 0xc);
+ const Relocation& reloc4 = sect1.relocations[3];
+ EXPECT_EQ(reloc4.offset, 0x2U);
+ EXPECT_TRUE(reloc4.scattered);
+ EXPECT_EQ((int)reloc4.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF);
+ EXPECT_EQ(reloc4.length, 2);
+ EXPECT_FALSE(reloc4.pcRel);
+ EXPECT_EQ(reloc4.symbol, 0U);
+ EXPECT_EQ((int)(reloc4.value), 0x16);
+ const Relocation& reloc5 = sect1.relocations[4];
+ EXPECT_EQ(reloc5.offset, 0x0U);
+ EXPECT_TRUE(reloc5.scattered);
+ EXPECT_EQ((int)reloc5.type, (int)llvm::MachO::ARM_RELOC_PAIR);
+ EXPECT_EQ(reloc5.length, 2);
+ EXPECT_FALSE(reloc5.pcRel);
+ EXPECT_EQ(reloc5.symbol, 0U);
+ EXPECT_EQ((int)(reloc5.value), 0xc);
+
+ const Section& sect2 = f->sections[1];
+ EXPECT_TRUE(sect2.segmentName.equals("__TEXT"));
+ EXPECT_TRUE(sect2.sectionName.equals("__cstring"));
+ EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS));
+ EXPECT_EQ((uint32_t)(sect2.attributes), 0U);
+ EXPECT_EQ((uint16_t)sect2.alignment, 1U);
+ EXPECT_EQ((uint64_t)sect2.address, 0x016ULL);
+ EXPECT_EQ(sect2.content.size(), 7UL);
+ EXPECT_EQ((int)(sect2.content[0]), 0x68);
+ EXPECT_EQ((int)(sect2.content[1]), 0x65);
+ EXPECT_EQ((int)(sect2.content[2]), 0x6c);
+
+ EXPECT_EQ(f->globalSymbols.size(), 1UL);
+ const Symbol& sym1 = f->globalSymbols[0];
+ EXPECT_TRUE(sym1.name.equals("_main"));
+ EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT);
+ EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT);
+ EXPECT_EQ(sym1.sect, 1);
+ EXPECT_EQ((int)(sym1.desc), (int)(llvm::MachO::N_ARM_THUMB_DEF));
+ EXPECT_EQ((uint64_t)sym1.value, 0x0ULL);
+ EXPECT_EQ(f->undefinedSymbols.size(), 1UL);
+ const Symbol& sym2 = f->undefinedSymbols[0];
+ EXPECT_TRUE(sym2.name.equals("_printf"));
+ EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF);
+ EXPECT_EQ((int)(sym2.scope), 0);
+ EXPECT_EQ(sym2.sect, 0);
+ EXPECT_EQ((int)(sym2.desc), 0);
+ EXPECT_EQ((uint64_t)sym2.value, 0x0ULL);
+}
--- /dev/null
+0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x07, 0x00,
+0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00,
+0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x40,
+0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
+0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00,
+0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x98, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
+0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00, 0x09,
+0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00,
+0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00,
+0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74,
+0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
--- /dev/null
+//===-- LibCxxOptional.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class OptionalFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ OptionalFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_has_value ? 1U : 0U; }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ /// True iff the option contains a value.
+ bool m_has_value = false;
+};
+} // namespace
+
+bool OptionalFrontEnd::Update() {
+ ValueObjectSP engaged_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true));
+
+ if (!engaged_sp)
+ return false;
+
+ // __engaged_ is a bool flag and is true if the optional contains a value.
+ // Converting it to unsigned gives us a size of 1 if it contains a value
+ // and 0 if not.
+ m_has_value = engaged_sp->GetValueAsUnsigned(0) == 1;
+
+ return false;
+}
+
+ValueObjectSP OptionalFrontEnd::GetChildAtIndex(size_t idx) {
+ if (!m_has_value)
+ return ValueObjectSP();
+
+ // __val_ contains the underlying value of an optional if it has one.
+ // Currently because it is part of an anonymous union GetChildMemberWithName()
+ // does not peer through and find it unless we are at the parent itself.
+ // We can obtain the parent through __engaged_.
+ ValueObjectSP val_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true)
+ ->GetParent()
+ ->GetChildAtIndex(0, true)
+ ->GetChildMemberWithName(ConstString("__val_"), true));
+
+ if (!val_sp)
+ return ValueObjectSP();
+
+ CompilerType holder_type = val_sp->GetCompilerType();
+
+ if (!holder_type)
+ return ValueObjectSP();
+
+ return val_sp->Clone(ConstString("Value"));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new OptionalFrontEnd(*valobj_sp);
+ return nullptr;
+}
--- /dev/null
+//===-- lldb.cpp ----------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "VCSVersion.inc"
+#include "lldb/lldb-private.h"
+#include "clang/Basic/Version.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// LLDB_VERSION_STRING is set through a define so unlike the other defines
+// expanded with CMake, it lacks the double quotes.
+#define QUOTE(str) #str
+#define EXPAND_AND_QUOTE(str) QUOTE(str)
+
+static const char *GetLLDBVersion() {
+#ifdef LLDB_VERSION_STRING
+ return EXPAND_AND_QUOTE(LLDB_VERSION_STRING);
+#else
+ return "lldb version " CLANG_VERSION_STRING;
+#endif
+}
+
+static const char *GetLLDBRevision() {
+#ifdef LLDB_REVISION
+ return LLDB_REVISION;
+#else
+ return NULL;
+#endif
+}
+
+static const char *GetLLDBRepository() {
+#ifdef LLDB_REPOSITORY
+ return LLDB_REPOSITORY;
+#else
+ return NULL;
+#endif
+}
+
+const char *lldb_private::GetVersion() {
+ static std::string g_version_str;
+ if (g_version_str.empty()) {
+ const char *lldb_version = GetLLDBVersion();
+ const char *lldb_repo = GetLLDBRepository();
+ const char *lldb_rev = GetLLDBRevision();
+ g_version_str += lldb_version;
+ if (lldb_repo || lldb_rev) {
+ g_version_str += " (";
+ if (lldb_repo)
+ g_version_str += lldb_repo;
+ if (lldb_repo && lldb_rev)
+ g_version_str += " ";
+ if (lldb_rev) {
+ g_version_str += "revision ";
+ g_version_str += lldb_rev;
+ }
+ g_version_str += ")";
+ }
+
+ std::string clang_rev(clang::getClangRevision());
+ if (clang_rev.length() > 0) {
+ g_version_str += "\n clang revision ";
+ g_version_str += clang_rev;
+ }
+ std::string llvm_rev(clang::getLLVMRevision());
+ if (llvm_rev.length() > 0) {
+ g_version_str += "\n llvm revision ";
+ g_version_str += llvm_rev;
+ }
+ }
+ return g_version_str.c_str();
+}
--- /dev/null
+CXX_SOURCES := main.cpp
+
+USE_LIBCPP := 1
+
+include Makefile.rules
--- /dev/null
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibcxxDequeDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @add_test_categories(["libc++"])
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here",
+ lldb.SBFileSpec("main.cpp"))
+
+ self.expect_expr("empty", result_children=[])
+ self.expect_expr("deque_1", result_children=[
+ ValueCheck(name="[0]", value="1"),
+ ])
+ self.expect_expr("deque_3", result_children=[
+ ValueCheck(name="[0]", value="3"),
+ ValueCheck(name="[1]", value="1"),
+ ValueCheck(name="[2]", value="2")
+ ])
--- /dev/null
+#include <deque>
+
+int main() {
+ std::deque<int> empty;
+ std::deque<int> deque_1 = {1};
+ std::deque<int> deque_3 = {3, 1, 2};
+ return empty.size() + deque_1.front() + deque_3.front(); // break here
+}
--- /dev/null
+CXX_SOURCES := main.cpp
+
+USE_LIBCPP := 1
+
+CXXFLAGS_EXTRAS := -O0
+include Makefile.rules
--- /dev/null
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibcxxListDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line number to break at.
+ self.line = line_number('main.cpp', '// Set break point at this line.')
+ self.line2 = line_number('main.cpp',
+ '// Set second break point at this line.')
+ self.line3 = line_number('main.cpp',
+ '// Set third break point at this line.')
+ self.line4 = line_number('main.cpp',
+ '// Set fourth break point at this line.')
+
+ @add_test_categories(["libc++"])
+ def test_with_run_command(self):
+ """Test that that file and class static variables display correctly."""
+ self.build()
+ self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.line, num_expected_locations=-1)
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.line2, num_expected_locations=-1)
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.line3, num_expected_locations=-1)
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.line4, num_expected_locations=-1)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ lldbutil.skip_if_library_missing(
+ self, self.target(), lldbutil.PrintableRegex("libc\+\+"))
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ # This is the function to remove the custom formats in order to have a
+ # clean slate for the next test case.
+ def cleanup():
+ self.runCmd('type format clear', check=False)
+ self.runCmd('type summary clear', check=False)
+ self.runCmd('type filter clear', check=False)
+ self.runCmd('type synth clear', check=False)
+ self.runCmd(
+ "settings set target.max-children-count 256",
+ check=False)
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ self.runCmd("frame variable numbers_list --show-types")
+ self.runCmd(
+ "type summary add std::int_list std::string_list int_list string_list --summary-string \"list has ${svar%#} items\" -e")
+ self.runCmd("type format add -f hex int")
+
+ self.expect("frame variable numbers_list --raw", matching=False,
+ substrs=['list has 0 items',
+ '{}'])
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 0 items',
+ '{}'])
+
+ self.expect("p numbers_list",
+ substrs=['list has 0 items',
+ '{}'])
+
+ self.runCmd("n") # This gets up past the printf
+ self.runCmd("n") # Now advance over the first push_back.
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 1 items',
+ '[0] = ',
+ '0x12345678'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 4 items',
+ '[0] = ',
+ '0x12345678',
+ '[1] =',
+ '0x11223344',
+ '[2] =',
+ '0xbeeffeed',
+ '[3] =',
+ '0x00abba00'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 6 items',
+ '[0] = ',
+ '0x12345678',
+ '0x11223344',
+ '0xbeeffeed',
+ '0x00abba00',
+ '[4] =',
+ '0x0abcdef0',
+ '[5] =',
+ '0x0cab0cab'])
+
+ self.expect("p numbers_list",
+ substrs=['list has 6 items',
+ '[0] = ',
+ '0x12345678',
+ '0x11223344',
+ '0xbeeffeed',
+ '0x00abba00',
+ '[4] =',
+ '0x0abcdef0',
+ '[5] =',
+ '0x0cab0cab'])
+
+ # check access-by-index
+ self.expect("frame variable numbers_list[0]",
+ substrs=['0x12345678'])
+ self.expect("frame variable numbers_list[1]",
+ substrs=['0x11223344'])
+
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 0 items',
+ '{}'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['list has 4 items',
+ '[0] = ', '1',
+ '[1] = ', '2',
+ '[2] = ', '3',
+ '[3] = ', '4'])
+
+ ListPtr = self.frame().FindVariable("list_ptr")
+ self.assertTrue(ListPtr.GetChildAtIndex(
+ 0).GetValueAsUnsigned(0) == 1, "[0] = 1")
+
+ # check that MightHaveChildren() gets it right
+ self.assertTrue(
+ self.frame().FindVariable("numbers_list").MightHaveChildren(),
+ "numbers_list.MightHaveChildren() says False for non empty!")
+
+ self.runCmd("type format delete int")
+
+ self.runCmd("c")
+
+ self.expect("frame variable text_list",
+ substrs=['list has 3 items',
+ '[0]', 'goofy',
+ '[1]', 'is',
+ '[2]', 'smart'])
+
+ # check that MightHaveChildren() gets it right
+ self.assertTrue(
+ self.frame().FindVariable("text_list").MightHaveChildren(),
+ "text_list.MightHaveChildren() says False for non empty!")
+
+ self.expect("p text_list",
+ substrs=['list has 3 items',
+ '\"goofy\"',
+ '\"is\"',
+ '\"smart\"'])
+
+ self.runCmd("n") # This gets us past the printf
+ self.runCmd("n")
+ self.runCmd("n")
+
+ # check access-by-index
+ self.expect("frame variable text_list[0]",
+ substrs=['goofy'])
+ self.expect("frame variable text_list[3]",
+ substrs=['!!!'])
+
+ self.runCmd("continue")
+
+ # check that the list provider correctly updates if elements move
+ countingList = self.frame().FindVariable("countingList")
+ countingList.SetPreferDynamicValue(True)
+ countingList.SetPreferSyntheticValue(True)
+
+ self.assertTrue(countingList.GetChildAtIndex(
+ 0).GetValueAsUnsigned(0) == 3141, "list[0] == 3141")
+ self.assertTrue(countingList.GetChildAtIndex(
+ 1).GetValueAsUnsigned(0) == 3141, "list[1] == 3141")
+
+ self.runCmd("continue")
+
+ self.assertEqual(
+ countingList.GetChildAtIndex(0).GetValueAsUnsigned(0), 3141,
+ "uniqued list[0] == 3141")
+ self.assertEqual(
+ countingList.GetChildAtIndex(1).GetValueAsUnsigned(0), 3142,
+ "uniqued list[1] == 3142")
--- /dev/null
+CXX_SOURCES := main.cpp
+
+USE_LIBCPP := 1
+
+CXXFLAGS_EXTRAS := -O0
+include Makefile.rules
--- /dev/null
+"""
+Test that the debugger handles loops in std::list (which can appear as a result of e.g. memory
+corruption).
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibcxxListDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @add_test_categories(["libc++"])
+ @expectedFailureAndroid(bugnumber="llvm.org/pr32592")
+ def test_with_run_command(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target and target.IsValid(), "Target is valid")
+
+ file_spec = lldb.SBFileSpec("main.cpp", False)
+ breakpoint1 = target.BreakpointCreateBySourceRegex(
+ '// Set break point at this line.', file_spec)
+ self.assertTrue(breakpoint1 and breakpoint1.IsValid())
+ breakpoint2 = target.BreakpointCreateBySourceRegex(
+ '// Set second break point at this line.', file_spec)
+ self.assertTrue(breakpoint2 and breakpoint2.IsValid())
+
+ # Run the program, it should stop at breakpoint 1.
+ process = target.LaunchSimple(
+ None, None, self.get_process_working_directory())
+ self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID)
+ self.assertEqual(
+ len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint1)), 1)
+
+ # verify our list is displayed correctly
+ self.expect(
+ "frame variable *numbers_list",
+ substrs=[
+ '[0] = 1',
+ '[1] = 2',
+ '[2] = 3',
+ '[3] = 4',
+ '[5] = 6'])
+
+ # Continue to breakpoint 2.
+ process.Continue()
+ self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID)
+ self.assertEqual(
+ len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint2)), 1)
+
+ # The list is now inconsistent. However, we should be able to get the first three
+ # elements at least (and most importantly, not crash).
+ self.expect(
+ "frame variable *numbers_list",
+ substrs=[
+ '[0] = 1',
+ '[1] = 2',
+ '[2] = 3'])
+
+ # Run to completion.
+ process.Continue()
+ self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
--- /dev/null
+// Evil hack: To simulate memory corruption, we want to fiddle with some internals of std::list.
+// Make those accessible to us.
+#define private public
+#define protected public
+
+#include <list>
+#include <stdio.h>
+#include <assert.h>
+
+typedef std::list<int> int_list;
+
+int main()
+{
+#ifdef LLDB_USING_LIBCPP
+ int_list *numbers_list = new int_list{1,2,3,4,5,6,7,8,9,10};
+
+ printf("// Set break point at this line.");
+
+#if _LIBCPP_VERSION >= 3800
+ auto *third_elem = numbers_list->__end_.__next_->__next_->__next_;
+ assert(third_elem->__as_node()->__value_ == 3);
+ auto *fifth_elem = third_elem->__next_->__next_;
+ assert(fifth_elem->__as_node()->__value_ == 5);
+#else
+ auto *third_elem = numbers_list->__end_.__next_->__next_->__next_;
+ assert(third_elem->__value_ == 3);
+ auto *fifth_elem = third_elem->__next_->__next_;
+ assert(fifth_elem->__value_ == 5);
+#endif
+ fifth_elem->__next_ = third_elem;
+#endif
+
+ // Any attempt to free the list will probably crash the program. Let's just leak it.
+ return 0; // Set second break point at this line.
+}
--- /dev/null
+#include <string>
+#include <list>
+#include <stdio.h>
+
+typedef std::list<int> int_list;
+typedef std::list<std::string> string_list;
+
+int main()
+{
+ int_list numbers_list;
+ std::list<int>* list_ptr = &numbers_list;
+
+ printf("// Set break point at this line.");
+ (numbers_list.push_back(0x12345678));
+ (numbers_list.push_back(0x11223344));
+ (numbers_list.push_back(0xBEEFFEED));
+ (numbers_list.push_back(0x00ABBA00));
+ (numbers_list.push_back(0x0ABCDEF0));
+ (numbers_list.push_back(0x0CAB0CAB));
+
+ numbers_list.clear();
+
+ (numbers_list.push_back(1));
+ (numbers_list.push_back(2));
+ (numbers_list.push_back(3));
+ (numbers_list.push_back(4));
+
+ string_list text_list;
+ (text_list.push_back(std::string("goofy")));
+ (text_list.push_back(std::string("is")));
+ (text_list.push_back(std::string("smart")));
+
+ printf("// Set second break point at this line.");
+ (text_list.push_back(std::string("!!!")));
+
+ std::list<int> countingList = {3141, 3142, 3142,3142,3142, 3142, 3142, 3141};
+ countingList.sort();
+ printf("// Set third break point at this line.");
+ countingList.unique();
+ printf("// Set fourth break point at this line.");
+ countingList.size();
+
+ return 0;
+}
--- /dev/null
+CXX_SOURCES := main.cpp
+
+USE_LIBCPP := 1
+
+CXXFLAGS_EXTRAS := -std=c++17 -fno-exceptions
+include Makefile.rules
--- /dev/null
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibcxxOptionalDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @add_test_categories(["libc++"])
+ ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
+ ## with -std=c++17.
+ @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '7.0'])
+ ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
+ @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
+
+ def test_with_run_command(self):
+ """Test that that file and class static variables display correctly."""
+ self.build()
+ self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+ bkpt = self.target().FindBreakpointByID(
+ lldbutil.run_break_set_by_source_regexp(
+ self, "break here"))
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ self.runCmd( "frame variable has_optional" )
+
+ output = self.res.GetOutput()
+
+ ## The variable has_optional tells us if the test program
+ ## detected we have a sufficient libc++ version to support optional
+ ## false means we do not and therefore should skip the test
+ if output.find("(bool) has_optional = false") != -1 :
+ self.skipTest( "Optional not supported" )
+
+ lldbutil.continue_to_breakpoint(self.process(), bkpt)
+
+ self.expect("frame variable number_not_engaged",
+ substrs=['Has Value=false'])
+
+ self.expect("frame variable number_engaged",
+ substrs=['Has Value=true',
+ 'Value = 42',
+ '}'])
+
+ self.expect("frame var numbers",
+ substrs=['(optional_int_vect) numbers = Has Value=true {',
+ 'Value = size=4 {',
+ '[0] = 1',
+ '[1] = 2',
+ '[2] = 3',
+ '[3] = 4',
+ '}',
+ '}'])
+
+ self.expect("frame var ostring",
+ substrs=['(optional_string) ostring = Has Value=true {',
+ 'Value = "hello"',
+ '}'])
--- /dev/null
+#include <cstdio>
+#include <string>
+#include <vector>
+
+// If we have libc++ 4.0 or greater we should have <optional>
+// According to libc++ C++1z status page https://libcxx.llvm.org/cxx1z_status.html
+#if _LIBCPP_VERSION >= 4000
+#include <optional>
+#define HAVE_OPTIONAL 1
+#else
+#define HAVE_OPTIONAL 0
+#endif
+
+
+int main()
+{
+ bool has_optional = HAVE_OPTIONAL ;
+
+ printf( "%d\n", has_optional ) ; // break here
+
+#if HAVE_OPTIONAL == 1
+ using int_vect = std::vector<int> ;
+ using optional_int = std::optional<int> ;
+ using optional_int_vect = std::optional<int_vect> ;
+ using optional_string = std::optional<std::string> ;
+
+ optional_int number_not_engaged ;
+ optional_int number_engaged = 42 ;
+
+ printf( "%d\n", *number_engaged) ;
+
+ optional_int_vect numbers{{1,2,3,4}} ;
+
+ printf( "%d %d\n", numbers.value()[0], numbers.value()[1] ) ;
+
+ optional_string ostring = "hello" ;
+
+ printf( "%s\n", ostring->c_str() ) ;
+#endif
+
+ return 0; // break here
+}
--- /dev/null
+CXX_SOURCES := main.cpp
+
+# Work around "exception specification in declaration does not match previous
+# declaration" errors present in older libc++ releases. This error was fixed in
+# the 3.8 release.
+CFLAGS_EXTRAS := -fno-exceptions
+
+USE_LIBCPP := 1
+include Makefile.rules
--- /dev/null
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibcxxUnorderedDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.namespace = 'std'
+
+ @add_test_categories(["libc++"])
+ def test_with_run_command(self):
+ self.build()
+ self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_source_regexp(
+ self, "Set break point at this line.")
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ # This is the function to remove the custom formats in order to have a
+ # clean slate for the next test case.
+ def cleanup():
+ self.runCmd('type format clear', check=False)
+ self.runCmd('type summary clear', check=False)
+ self.runCmd('type filter clear', check=False)
+ self.runCmd('type synth clear', check=False)
+ self.runCmd(
+ "settings set target.max-children-count 256",
+ check=False)
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ ns = self.namespace
+ self.look_for_content_and_continue(
+ "map", ['%s::unordered_map' %
+ ns, 'size=5 {', 'hello', 'world', 'this', 'is', 'me'])
+
+ self.look_for_content_and_continue(
+ "mmap", ['%s::unordered_multimap' % ns, 'size=6 {', 'first = 3', 'second = "this"',
+ 'first = 2', 'second = "hello"'])
+
+ self.look_for_content_and_continue(
+ "iset", ['%s::unordered_set' %
+ ns, 'size=5 {', '\[\d\] = 5', '\[\d\] = 3', '\[\d\] = 2'])
+
+ self.look_for_content_and_continue(
+ "sset", ['%s::unordered_set' % ns, 'size=5 {', '\[\d\] = "is"', '\[\d\] = "world"',
+ '\[\d\] = "hello"'])
+
+ self.look_for_content_and_continue(
+ "imset", ['%s::unordered_multiset' % ns, 'size=6 {', '(\[\d\] = 3(\\n|.)+){3}',
+ '\[\d\] = 2', '\[\d\] = 1'])
+
+ self.look_for_content_and_continue(
+ "smset", ['%s::unordered_multiset' % ns, 'size=5 {', '(\[\d\] = "is"(\\n|.)+){2}',
+ '(\[\d\] = "world"(\\n|.)+){2}'])
+
+ def look_for_content_and_continue(self, var_name, patterns):
+ self.expect(("frame variable %s" % var_name), patterns=patterns)
+ self.expect(("frame variable %s" % var_name), patterns=patterns)
+ self.runCmd("continue")
--- /dev/null
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+using std::string;
+
+#define intstr_map std::unordered_map<int, string>
+#define intstr_mmap std::unordered_multimap<int, string>
+
+#define int_set std::unordered_set<int>
+#define str_set std::unordered_set<string>
+#define int_mset std::unordered_multiset<int>
+#define str_mset std::unordered_multiset<string>
+
+int g_the_foo = 0;
+
+int thefoo_rw(int arg = 1)
+{
+ if (arg < 0)
+ arg = 0;
+ if (!arg)
+ arg = 1;
+ g_the_foo += arg;
+ return g_the_foo;
+}
+
+int main()
+{
+ intstr_map map;
+ map.emplace(1,"hello");
+ map.emplace(2,"world");
+ map.emplace(3,"this");
+ map.emplace(4,"is");
+ map.emplace(5,"me");
+ thefoo_rw(); // Set break point at this line.
+
+ intstr_mmap mmap;
+ mmap.emplace(1,"hello");
+ mmap.emplace(2,"hello");
+ mmap.emplace(2,"world");
+ mmap.emplace(3,"this");
+ mmap.emplace(3,"this");
+ mmap.emplace(3,"this");
+ thefoo_rw(); // Set break point at this line.
+
+ int_set iset;
+ iset.emplace(1);
+ iset.emplace(2);
+ iset.emplace(3);
+ iset.emplace(4);
+ iset.emplace(5);
+ thefoo_rw(); // Set break point at this line.
+
+ str_set sset;
+ sset.emplace("hello");
+ sset.emplace("world");
+ sset.emplace("this");
+ sset.emplace("is");
+ sset.emplace("me");
+ thefoo_rw(); // Set break point at this line.
+
+ int_mset imset;
+ imset.emplace(1);
+ imset.emplace(2);
+ imset.emplace(2);
+ imset.emplace(3);
+ imset.emplace(3);
+ imset.emplace(3);
+ thefoo_rw(); // Set break point at this line.
+
+ str_mset smset;
+ smset.emplace("hello");
+ smset.emplace("world");
+ smset.emplace("world");
+ smset.emplace("is");
+ smset.emplace("is");
+ thefoo_rw(); // Set break point at this line.
+
+ return 0;
+}
--- /dev/null
+CXX_SOURCES := main.cpp
+
+CFLAGS_EXTRAS := -O0
+USE_LIBSTDCPP := 1
+
+include Makefile.rules
--- /dev/null
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class StdListDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line numbers to break at for the different tests.
+ self.line = line_number('main.cpp', '// Set break point at this line.')
+ self.optional_line = line_number(
+ 'main.cpp', '// Optional break point at this line.')
+ self.final_line = line_number(
+ 'main.cpp', '// Set final break point at this line.')
+
+ @add_test_categories(["libstdcxx"])
+ @expectedFailureAll(bugnumber="llvm.org/pr50861", compiler="gcc")
+ def test_with_run_command(self):
+ """Test that that file and class static variables display correctly."""
+ self.build()
+ self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.line, num_expected_locations=-1)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ # This is the function to remove the custom formats in order to have a
+ # clean slate for the next test case.
+ def cleanup():
+ self.runCmd('type format clear', check=False)
+ self.runCmd('type summary clear', check=False)
+ self.runCmd('type filter clear', check=False)
+ self.runCmd('type synth clear', check=False)
+ self.runCmd(
+ "settings set target.max-children-count 256",
+ check=False)
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ self.runCmd("frame variable numbers_list --show-types")
+
+ self.runCmd("type format add -f hex int")
+
+ self.expect("frame variable numbers_list --raw", matching=False,
+ substrs=['size=0',
+ '{}'])
+ self.expect(
+ "frame variable &numbers_list._M_impl._M_node --raw",
+ matching=False,
+ substrs=[
+ 'size=0',
+ '{}'])
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.expect("p numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=1',
+ '[0] = ',
+ '0x12345678'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=4',
+ '[0] = ',
+ '0x12345678',
+ '[1] =',
+ '0x11223344',
+ '[2] =',
+ '0xbeeffeed',
+ '[3] =',
+ '0x00abba00'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=6',
+ '[0] = ',
+ '0x12345678',
+ '0x11223344',
+ '0xbeeffeed',
+ '0x00abba00',
+ '[4] =',
+ '0x0abcdef0',
+ '[5] =',
+ '0x0cab0cab'])
+
+ self.expect("p numbers_list",
+ substrs=['size=6',
+ '[0] = ',
+ '0x12345678',
+ '0x11223344',
+ '0xbeeffeed',
+ '0x00abba00',
+ '[4] =',
+ '0x0abcdef0',
+ '[5] =',
+ '0x0cab0cab'])
+
+ # check access-by-index
+ self.expect("frame variable numbers_list[0]",
+ substrs=['0x12345678'])
+ self.expect("frame variable numbers_list[1]",
+ substrs=['0x11223344'])
+
+ # but check that expression does not rely on us
+ self.expect("expression numbers_list[0]", matching=False, error=True,
+ substrs=['0x12345678'])
+
+ # check that MightHaveChildren() gets it right
+ self.assertTrue(
+ self.frame().FindVariable("numbers_list").MightHaveChildren(),
+ "numbers_list.MightHaveChildren() says False for non empty!")
+
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=4',
+ '[0] = ', '1',
+ '[1] = ', '2',
+ '[2] = ', '3',
+ '[3] = ', '4'])
+
+ self.runCmd("type format delete int")
+
+ self.runCmd("n")
+
+ self.expect("frame variable text_list",
+ substrs=['size=0',
+ '{}'])
+
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.final_line, num_expected_locations=-1)
+
+ self.runCmd("c", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ self.expect("frame variable text_list",
+ substrs=['size=4',
+ '[0]', 'goofy',
+ '[1]', 'is',
+ '[2]', 'smart',
+ '[3]', '!!!'])
+
+ self.expect("p text_list",
+ substrs=['size=4',
+ '\"goofy\"',
+ '\"is\"',
+ '\"smart\"',
+ '\"!!!\"'])
+
+ # check access-by-index
+ self.expect("frame variable text_list[0]",
+ substrs=['goofy'])
+ self.expect("frame variable text_list[3]",
+ substrs=['!!!'])
+
+ # but check that expression does not rely on us
+ self.expect("expression text_list[0]", matching=False, error=True,
+ substrs=['goofy'])
+
+ # check that MightHaveChildren() gets it right
+ self.assertTrue(
+ self.frame().FindVariable("text_list").MightHaveChildren(),
+ "text_list.MightHaveChildren() says False for non empty!")
--- /dev/null
+#include <list>
+#include <string>
+
+typedef std::list<int> int_list;
+typedef std::list<std::string> string_list;
+
+int main()
+{
+ int_list numbers_list;
+
+ numbers_list.push_back(0x12345678); // Set break point at this line.
+ numbers_list.push_back(0x11223344);
+ numbers_list.push_back(0xBEEFFEED);
+ numbers_list.push_back(0x00ABBA00);
+ numbers_list.push_back(0x0ABCDEF0);
+ numbers_list.push_back(0x0CAB0CAB);
+
+ numbers_list.clear();
+
+ numbers_list.push_back(1);
+ numbers_list.push_back(2);
+ numbers_list.push_back(3);
+ numbers_list.push_back(4);
+
+ string_list text_list;
+ text_list.push_back(std::string("goofy")); // Optional break point at this line.
+ text_list.push_back(std::string("is"));
+ text_list.push_back(std::string("smart"));
+
+ text_list.push_back(std::string("!!!"));
+
+ return 0; // Set final break point at this line.
+}
+
--- /dev/null
+import ctypes
+import errno
+import io
+import os
+import os.path
+import threading
+import socket
+import lldb
+import binascii
+import traceback
+from lldbsuite.support import seven
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbtest_config
+
+
+def checksum(message):
+ """
+ Calculate the GDB server protocol checksum of the message.
+
+ The GDB server protocol uses a simple modulo 256 sum.
+ """
+ check = 0
+ for c in message:
+ check += ord(c)
+ return check % 256
+
+
+def frame_packet(message):
+ """
+ Create a framed packet that's ready to send over the GDB connection
+ channel.
+
+ Framing includes surrounding the message between $ and #, and appending
+ a two character hex checksum.
+ """
+ return "$%s#%02x" % (message, checksum(message))
+
+
+def escape_binary(message):
+ """
+ Escape the binary message using the process described in the GDB server
+ protocol documentation.
+
+ Most bytes are sent through as-is, but $, #, and { are escaped by writing
+ a { followed by the original byte mod 0x20.
+ """
+ out = ""
+ for c in message:
+ d = ord(c)
+ if d in (0x23, 0x24, 0x7d):
+ out += chr(0x7d)
+ out += chr(d ^ 0x20)
+ else:
+ out += c
+ return out
+
+
+def hex_encode_bytes(message):
+ """
+ Encode the binary message by converting each byte into a two-character
+ hex string.
+ """
+ out = ""
+ for c in message:
+ out += "%02x" % ord(c)
+ return out
+
+
+def hex_decode_bytes(hex_bytes):
+ """
+ Decode the hex string into a binary message by converting each two-character
+ hex string into a single output byte.
+ """
+ out = ""
+ hex_len = len(hex_bytes)
+ while i < hex_len - 1:
+ out += chr(int(hex_bytes[i:i + 2]), 16)
+ i += 2
+ return out
+
+
+class MockGDBServerResponder:
+ """
+ A base class for handling client packets and issuing server responses for
+ GDB tests.
+
+ This handles many typical situations, while still allowing subclasses to
+ completely customize their responses.
+
+ Most subclasses will be interested in overriding the other() method, which
+ handles any packet not recognized in the common packet handling code.
+ """
+
+ registerCount = 40
+ packetLog = None
+
+ def __init__(self):
+ self.packetLog = []
+
+ def respond(self, packet):
+ """
+ Return the unframed packet data that the server should issue in response
+ to the given packet received from the client.
+ """
+ self.packetLog.append(packet)
+ if packet is MockGDBServer.PACKET_INTERRUPT:
+ return self.interrupt()
+ if packet == "c":
+ return self.cont()
+ if packet.startswith("vCont;c"):
+ return self.vCont(packet)
+ if packet[0] == "A":
+ return self.A(packet)
+ if packet[0] == "D":
+ return self.D(packet)
+ if packet[0] == "g":
+ return self.readRegisters()
+ if packet[0] == "G":
+ # Gxxxxxxxxxxx
+ # Gxxxxxxxxxxx;thread:1234;
+ return self.writeRegisters(packet[1:].split(';')[0])
+ if packet[0] == "p":
+ regnum = packet[1:].split(';')[0]
+ return self.readRegister(int(regnum, 16))
+ if packet[0] == "P":
+ register, value = packet[1:].split("=")
+ return self.writeRegister(int(register, 16), value)
+ if packet[0] == "m":
+ addr, length = [int(x, 16) for x in packet[1:].split(',')]
+ return self.readMemory(addr, length)
+ if packet[0] == "M":
+ location, encoded_data = packet[1:].split(":")
+ addr, length = [int(x, 16) for x in location.split(',')]
+ return self.writeMemory(addr, encoded_data)
+ if packet[0:7] == "qSymbol":
+ return self.qSymbol(packet[8:])
+ if packet[0:10] == "qSupported":
+ return self.qSupported(packet[11:].split(";"))
+ if packet == "qfThreadInfo":
+ return self.qfThreadInfo()
+ if packet == "qsThreadInfo":
+ return self.qsThreadInfo()
+ if packet == "qC":
+ return self.qC()
+ if packet == "QEnableErrorStrings":
+ return self.QEnableErrorStrings()
+ if packet == "?":
+ return self.haltReason()
+ if packet == "s":
+ return self.haltReason()
+ if packet[0] == "H":
+ tid = packet[2:]
+ if "." in tid:
+ assert tid.startswith("p")
+ # TODO: do we want to do anything with PID?
+ tid = tid.split(".", 1)[1]
+ return self.selectThread(packet[1], int(tid, 16))
+ if packet[0:6] == "qXfer:":
+ obj, read, annex, location = packet[6:].split(":")
+ offset, length = [int(x, 16) for x in location.split(',')]
+ data, has_more = self.qXferRead(obj, annex, offset, length)
+ if data is not None:
+ return self._qXferResponse(data, has_more)
+ return ""
+ if packet.startswith("vAttach;"):
+ pid = packet.partition(';')[2]
+ return self.vAttach(int(pid, 16))
+ if packet[0] == "Z":
+ return self.setBreakpoint(packet)
+ if packet.startswith("qThreadStopInfo"):
+ threadnum = int (packet[15:], 16)
+ return self.threadStopInfo(threadnum)
+ if packet == "QThreadSuffixSupported":
+ return self.QThreadSuffixSupported()
+ if packet == "QListThreadsInStopReply":
+ return self.QListThreadsInStopReply()
+ if packet.startswith("qMemoryRegionInfo:"):
+ return self.qMemoryRegionInfo(int(packet.split(':')[1], 16))
+ if packet == "qQueryGDBServer":
+ return self.qQueryGDBServer()
+ if packet == "qHostInfo":
+ return self.qHostInfo()
+ if packet == "qGetWorkingDir":
+ return self.qGetWorkingDir()
+ if packet == "qOffsets":
+ return self.qOffsets();
+ if packet == "qsProcessInfo":
+ return self.qsProcessInfo()
+ if packet.startswith("qfProcessInfo"):
+ return self.qfProcessInfo(packet)
+ if packet.startswith("qPathComplete:"):
+ return self.qPathComplete()
+ if packet.startswith("vFile:"):
+ return self.vFile(packet)
+ if packet.startswith("vRun;"):
+ return self.vRun(packet)
+ if packet.startswith("qLaunchSuccess"):
+ return self.qLaunchSuccess()
+ if packet.startswith("QEnvironment:"):
+ return self.QEnvironment(packet)
+ if packet.startswith("QEnvironmentHexEncoded:"):
+ return self.QEnvironmentHexEncoded(packet)
+ if packet.startswith("qRegisterInfo"):
+ regnum = int(packet[len("qRegisterInfo"):], 16)
+ return self.qRegisterInfo(regnum)
+ if packet == "k":
+ return self.k()
+
+ return self.other(packet)
+
+ def qsProcessInfo(self):
+ return "E04"
+
+ def qfProcessInfo(self, packet):
+ return "E04"
+
+ def qGetWorkingDir(self):
+ return "2f"
+
+ def qOffsets(self):
+ return ""
+
+ def qHostInfo(self):
+ return "ptrsize:8;endian:little;"
+
+ def qQueryGDBServer(self):
+ return "E04"
+
+ def interrupt(self):
+ raise self.UnexpectedPacketException()
+
+ def cont(self):
+ raise self.UnexpectedPacketException()
+
+ def vCont(self, packet):
+ raise self.UnexpectedPacketException()
+
+ def A(self, packet):
+ return ""
+
+ def D(self, packet):
+ return "OK"
+
+ def readRegisters(self):
+ return "00000000" * self.registerCount
+
+ def readRegister(self, register):
+ return "00000000"
+
+ def writeRegisters(self, registers_hex):
+ return "OK"
+
+ def writeRegister(self, register, value_hex):
+ return "OK"
+
+ def readMemory(self, addr, length):
+ return "00" * length
+
+ def writeMemory(self, addr, data_hex):
+ return "OK"
+
+ def qSymbol(self, symbol_args):
+ return "OK"
+
+ def qSupported(self, client_supported):
+ return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+"
+
+ def qfThreadInfo(self):
+ return "l"
+
+ def qsThreadInfo(self):
+ return "l"
+
+ def qC(self):
+ return "QC0"
+
+ def QEnableErrorStrings(self):
+ return "OK"
+
+ def haltReason(self):
+ # SIGINT is 2, return type is 2 digit hex string
+ return "S02"
+
+ def qXferRead(self, obj, annex, offset, length):
+ return None, False
+
+ def _qXferResponse(self, data, has_more):
+ return "%s%s" % ("m" if has_more else "l", escape_binary(data))
+
+ def vAttach(self, pid):
+ raise self.UnexpectedPacketException()
+
+ def selectThread(self, op, thread_id):
+ return "OK"
+
+ def setBreakpoint(self, packet):
+ raise self.UnexpectedPacketException()
+
+ def threadStopInfo(self, threadnum):
+ return ""
+
+ def other(self, packet):
+ # empty string means unsupported
+ return ""
+
+ def QThreadSuffixSupported(self):
+ return ""
+
+ def QListThreadsInStopReply(self):
+ return ""
+
+ def qMemoryRegionInfo(self, addr):
+ return ""
+
+ def qPathComplete(self):
+ return ""
+
+ def vFile(self, packet):
+ return ""
+
+ def vRun(self, packet):
+ return ""
+
+ def qLaunchSuccess(self):
+ return ""
+
+ def QEnvironment(self, packet):
+ return "OK"
+
+ def QEnvironmentHexEncoded(self, packet):
+ return "OK"
+
+ def qRegisterInfo(self, num):
+ return ""
+
+ def k(self):
+ return ""
+
+ """
+ Raised when we receive a packet for which there is no default action.
+ Override the responder class to implement behavior suitable for the test at
+ hand.
+ """
+ class UnexpectedPacketException(Exception):
+ pass
+
+
+class ServerSocket:
+ """
+ A wrapper class for TCP or pty-based server.
+ """
+
+ def get_connect_address(self):
+ """Get address for the client to connect to."""
+
+ def get_connect_url(self):
+ """Get URL suitable for process connect command."""
+
+ def close_server(self):
+ """Close all resources used by the server."""
+
+ def accept(self):
+ """Accept a single client connection to the server."""
+
+ def close_connection(self):
+ """Close all resources used by the accepted connection."""
+
+ def recv(self):
+ """Receive a data packet from the connected client."""
+
+ def sendall(self, data):
+ """Send the data to the connected client."""
+
+
+class TCPServerSocket(ServerSocket):
+ def __init__(self):
+ family, type, proto, _, addr = socket.getaddrinfo(
+ "localhost", 0, proto=socket.IPPROTO_TCP)[0]
+ self._server_socket = socket.socket(family, type, proto)
+ self._connection = None
+
+ self._server_socket.bind(addr)
+ self._server_socket.listen(1)
+
+ def get_connect_address(self):
+ return "[{}]:{}".format(*self._server_socket.getsockname())
+
+ def get_connect_url(self):
+ return "connect://" + self.get_connect_address()
+
+ def close_server(self):
+ self._server_socket.close()
+
+ def accept(self):
+ assert self._connection is None
+ # accept() is stubborn and won't fail even when the socket is
+ # shutdown, so we'll use a timeout
+ self._server_socket.settimeout(30.0)
+ client, client_addr = self._server_socket.accept()
+ # The connected client inherits its timeout from self._socket,
+ # but we'll use a blocking socket for the client
+ client.settimeout(None)
+ self._connection = client
+
+ def close_connection(self):
+ assert self._connection is not None
+ self._connection.close()
+ self._connection = None
+
+ def recv(self):
+ assert self._connection is not None
+ return self._connection.recv(4096)
+
+ def sendall(self, data):
+ assert self._connection is not None
+ return self._connection.sendall(data)
+
+
+class PtyServerSocket(ServerSocket):
+ def __init__(self):
+ import pty
+ import tty
+ master, slave = pty.openpty()
+ tty.setraw(master)
+ self._master = io.FileIO(master, 'r+b')
+ self._slave = io.FileIO(slave, 'r+b')
+
+ def get_connect_address(self):
+ libc = ctypes.CDLL(None)
+ libc.ptsname.argtypes = (ctypes.c_int,)
+ libc.ptsname.restype = ctypes.c_char_p
+ return libc.ptsname(self._master.fileno()).decode()
+
+ def get_connect_url(self):
+ return "serial://" + self.get_connect_address()
+
+ def close_server(self):
+ self._slave.close()
+ self._master.close()
+
+ def recv(self):
+ try:
+ return self._master.read(4096)
+ except OSError as e:
+ # closing the pty results in EIO on Linux, convert it to EOF
+ if e.errno == errno.EIO:
+ return b''
+ raise
+
+ def sendall(self, data):
+ return self._master.write(data)
+
+
+class MockGDBServer:
+ """
+ A simple TCP-based GDB server that can test client behavior by receiving
+ commands and issuing custom-tailored responses.
+
+ Responses are generated via the .responder property, which should be an
+ instance of a class based on MockGDBServerResponder.
+ """
+
+ responder = None
+ _socket = None
+ _thread = None
+ _receivedData = None
+ _receivedDataOffset = None
+ _shouldSendAck = True
+
+ def __init__(self, socket_class):
+ self._socket_class = socket_class
+ self.responder = MockGDBServerResponder()
+
+ def start(self):
+ self._socket = self._socket_class()
+ # Start a thread that waits for a client connection.
+ self._thread = threading.Thread(target=self._run)
+ self._thread.start()
+
+ def stop(self):
+ self._socket.close_server()
+ self._thread.join()
+ self._thread = None
+
+ def get_connect_address(self):
+ return self._socket.get_connect_address()
+
+ def get_connect_url(self):
+ return self._socket.get_connect_url()
+
+ def _run(self):
+ # For testing purposes, we only need to worry about one client
+ # connecting just one time.
+ try:
+ self._socket.accept()
+ except:
+ return
+ self._shouldSendAck = True
+ self._receivedData = ""
+ self._receivedDataOffset = 0
+ data = None
+ while True:
+ try:
+ data = seven.bitcast_to_string(self._socket.recv())
+ if data is None or len(data) == 0:
+ break
+ self._receive(data)
+ except Exception as e:
+ print("An exception happened when receiving the response from the gdb server. Closing the client...")
+ traceback.print_exc()
+ self._socket.close_connection()
+ break
+
+ def _receive(self, data):
+ """
+ Collects data, parses and responds to as many packets as exist.
+ Any leftover data is kept for parsing the next time around.
+ """
+ self._receivedData += data
+ try:
+ packet = self._parsePacket()
+ while packet is not None:
+ self._handlePacket(packet)
+ packet = self._parsePacket()
+ except self.InvalidPacketException:
+ self._socket.close_connection()
+
+ def _parsePacket(self):
+ """
+ Reads bytes from self._receivedData, returning:
+ - a packet's contents if a valid packet is found
+ - the PACKET_ACK unique object if we got an ack
+ - None if we only have a partial packet
+
+ Raises an InvalidPacketException if unexpected data is received
+ or if checksums fail.
+
+ Once a complete packet is found at the front of self._receivedData,
+ its data is removed form self._receivedData.
+ """
+ data = self._receivedData
+ i = self._receivedDataOffset
+ data_len = len(data)
+ if data_len == 0:
+ return None
+ if i == 0:
+ # If we're looking at the start of the received data, that means
+ # we're looking for the start of a new packet, denoted by a $.
+ # It's also possible we'll see an ACK here, denoted by a +
+ if data[0] == '+':
+ self._receivedData = data[1:]
+ return self.PACKET_ACK
+ if ord(data[0]) == 3:
+ self._receivedData = data[1:]
+ return self.PACKET_INTERRUPT
+ if data[0] == '$':
+ i += 1
+ else:
+ raise self.InvalidPacketException(
+ "Unexpected leading byte: %s" % data[0])
+
+ # If we're looking beyond the start of the received data, then we're
+ # looking for the end of the packet content, denoted by a #.
+ # Note that we pick up searching from where we left off last time
+ while i < data_len and data[i] != '#':
+ i += 1
+
+ # If there isn't enough data left for a checksum, just remember where
+ # we left off so we can pick up there the next time around
+ if i > data_len - 3:
+ self._receivedDataOffset = i
+ return None
+
+ # If we have enough data remaining for the checksum, extract it and
+ # compare to the packet contents
+ packet = data[1:i]
+ i += 1
+ try:
+ check = int(data[i:i + 2], 16)
+ except ValueError:
+ raise self.InvalidPacketException("Checksum is not valid hex")
+ i += 2
+ if check != checksum(packet):
+ raise self.InvalidPacketException(
+ "Checksum %02x does not match content %02x" %
+ (check, checksum(packet)))
+ # remove parsed bytes from _receivedData and reset offset so parsing
+ # can start on the next packet the next time around
+ self._receivedData = data[i:]
+ self._receivedDataOffset = 0
+ return packet
+
+ def _handlePacket(self, packet):
+ if packet is self.PACKET_ACK:
+ # Ignore ACKs from the client. For the future, we can consider
+ # adding validation code to make sure the client only sends ACKs
+ # when it's supposed to.
+ return
+ response = ""
+ # We'll handle the ack stuff here since it's not something any of the
+ # tests will be concerned about, and it'll get turned off quickly anyway.
+ if self._shouldSendAck:
+ self._socket.sendall(seven.bitcast_to_bytes('+'))
+ if packet == "QStartNoAckMode":
+ self._shouldSendAck = False
+ response = "OK"
+ elif self.responder is not None:
+ # Delegate everything else to our responder
+ response = self.responder.respond(packet)
+ # Handle packet framing since we don't want to bother tests with it.
+ if response is not None:
+ framed = frame_packet(response)
+ self._socket.sendall(seven.bitcast_to_bytes(framed))
+
+ PACKET_ACK = object()
+ PACKET_INTERRUPT = object()
+
+ class InvalidPacketException(Exception):
+ pass
+
+
+class GDBRemoteTestBase(TestBase):
+ """
+ Base class for GDB client tests.
+
+ This class will setup and start a mock GDB server for the test to use.
+ It also provides assertPacketLogContains, which simplifies the checking
+ of packets sent by the client.
+ """
+
+ NO_DEBUG_INFO_TESTCASE = True
+ mydir = TestBase.compute_mydir(__file__)
+ server = None
+ server_socket_class = TCPServerSocket
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.server = MockGDBServer(socket_class=self.server_socket_class)
+ self.server.start()
+
+ def tearDown(self):
+ # TestBase.tearDown will kill the process, but we need to kill it early
+ # so its client connection closes and we can stop the server before
+ # finally calling the base tearDown.
+ if self.process() is not None:
+ self.process().Kill()
+ self.server.stop()
+ TestBase.tearDown(self)
+
+ def createTarget(self, yaml_path):
+ """
+ Create a target by auto-generating the object based on the given yaml
+ instructions.
+
+ This will track the generated object so it can be automatically removed
+ during tearDown.
+ """
+ yaml_base, ext = os.path.splitext(yaml_path)
+ obj_path = self.getBuildArtifact(yaml_base)
+ self.yaml2obj(yaml_path, obj_path)
+ return self.dbg.CreateTarget(obj_path)
+
+ def connect(self, target):
+ """
+ Create a process by connecting to the mock GDB server.
+
+ Includes assertions that the process was successfully created.
+ """
+ listener = self.dbg.GetListener()
+ error = lldb.SBError()
+ process = target.ConnectRemote(listener,
+ self.server.get_connect_url(), "gdb-remote", error)
+ self.assertTrue(error.Success(), error.description)
+ self.assertTrue(process, PROCESS_IS_VALID)
+ return process
+
+ def assertPacketLogContains(self, packets):
+ """
+ Assert that the mock server's packet log contains the given packets.
+
+ The packet log includes all packets sent by the client and received
+ by the server. This fuction makes it easy to verify that the client
+ sent the expected packets to the server.
+
+ The check does not require that the packets be consecutive, but does
+ require that they are ordered in the log as they ordered in the arg.
+ """
+ i = 0
+ j = 0
+ log = self.server.responder.packetLog
+
+ while i < len(packets) and j < len(log):
+ if log[j] == packets[i]:
+ i += 1
+ j += 1
+ if i < len(packets):
+ self.fail(u"Did not receive: %s\nLast 10 packets:\n\t%s" %
+ (packets[i], u'\n\t'.join(log)))
+
+
+class GDBPlatformClientTestBase(GDBRemoteTestBase):
+ """
+ Base class for platform server clients.
+
+ This class extends GDBRemoteTestBase by automatically connecting
+ via "platform connect" in the setUp() method.
+ """
+
+ def setUp(self):
+ super().setUp()
+ self.runCmd("platform select remote-gdb-server")
+ self.runCmd("platform connect " + self.server.get_connect_url())
+ self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected())
+
+ def tearDown(self):
+ self.dbg.GetSelectedPlatform().DisconnectRemote()
+ super().tearDown()
--- /dev/null
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
--- /dev/null
+"""
+Test the 'memory read' command.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class MemoryReadTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line number to break inside main().
+ self.line = line_number('main.cpp', '// Set break point at this line.')
+
+ def build_run_stop(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ # Break in main() after the variables are assigned values.
+ lldbutil.run_break_set_by_file_and_line(self,
+ "main.cpp",
+ self.line,
+ num_expected_locations=1,
+ loc_exact=True)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list",
+ STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped', 'stop reason = breakpoint'])
+
+ # The breakpoint should have a hit count of 1.
+ self.expect("breakpoint list -f",
+ BREAKPOINT_HIT_ONCE,
+ substrs=[' resolved, hit count = 1'])
+
+ @no_debug_info_test
+ def test_memory_read(self):
+ """Test the 'memory read' command with plain and vector formats."""
+ self.build_run_stop()
+
+ # (lldb) memory read -f d -c 1 `&argc`
+ # 0x7fff5fbff9a0: 1
+ self.runCmd("memory read -f d -c 1 `&argc`")
+
+ # Find the starting address for variable 'argc' to verify later that the
+ # '--format uint32_t[] --size 4 --count 4' option increments the address
+ # correctly.
+ line = self.res.GetOutput().splitlines()[0]
+ items = line.split(':')
+ address = int(items[0], 0)
+ argc = int(items[1], 0)
+ self.assertGreater(address, 0)
+ self.assertEquals(argc, 1)
+
+ # (lldb) memory read --format uint32_t[] --size 4 --count 4 `&argc`
+ # 0x7fff5fbff9a0: {0x00000001}
+ # 0x7fff5fbff9a4: {0x00000000}
+ # 0x7fff5fbff9a8: {0x0ec0bf27}
+ # 0x7fff5fbff9ac: {0x215db505}
+ self.runCmd(
+ "memory read --format uint32_t[] --size 4 --count 4 `&argc`")
+ lines = self.res.GetOutput().splitlines()
+ for i in range(4):
+ if i == 0:
+ # Verify that the printout for argc is correct.
+ self.assertEqual(
+ argc, int(lines[i].split(':')[1].strip(' {}'), 0))
+ addr = int(lines[i].split(':')[0], 0)
+ # Verify that the printout for addr is incremented correctly.
+ self.assertEqual(addr, (address + i * 4))
+
+ # (lldb) memory read --format char[] --size 7 --count 1 `&my_string`
+ # 0x7fff5fbff990: {abcdefg}
+ self.expect(
+ "memory read --format char[] --size 7 --count 1 `&my_string`",
+ substrs=['abcdefg'])
+
+ # (lldb) memory read --format 'hex float' --size 16 `&argc`
+ # 0x7fff5fbff5b0: error: unsupported byte size (16) for hex float
+ # format
+ self.expect(
+ "memory read --format 'hex float' --size 16 `&argc`",
+ substrs=['unsupported byte size (16) for hex float format'])
+
+ self.expect(
+ "memory read --format 'float' --count 1 --size 8 `&my_double`",
+ substrs=['1234.'])
+
+ # (lldb) memory read --format 'float' --count 1 --size 20 `&my_double`
+ # 0x7fff5fbff598: error: unsupported byte size (20) for float format
+ self.expect(
+ "memory read --format 'float' --count 1 --size 20 `&my_double`",
+ substrs=['unsupported byte size (20) for float format'])
+
+ self.expect('memory read --type int --count 5 `&my_ints[0]`',
+ substrs=['(int) 0x', '2', '4', '6', '8', '10'])
+
+ self.expect(
+ 'memory read --type int --count 5 --format hex `&my_ints[0]`',
+ substrs=[
+ '(int) 0x',
+ '0x',
+ '0a'])
+
+ self.expect(
+ 'memory read --type int --count 5 --offset 5 `&my_ints[0]`',
+ substrs=[
+ '(int) 0x',
+ '12',
+ '14',
+ '16',
+ '18',
+ '20'])
+
+ # the gdb format specifier and the size in characters for
+ # the returned values including the 0x prefix.
+ variations = [['b', 4], ['h', 6], ['w', 10], ['g', 18]]
+ for v in variations:
+ formatter = v[0]
+ expected_object_length = v[1]
+ self.runCmd(
+ "memory read --gdb-format 4%s &my_uint64s" % formatter)
+ lines = self.res.GetOutput().splitlines()
+ objects_read = []
+ for l in lines:
+ objects_read.extend(l.split(':')[1].split())
+ # Check that we got back 4 0x0000 etc bytes
+ for o in objects_read:
+ self.assertEqual(len(o), expected_object_length)
+ self.assertEquals(len(objects_read), 4)
+
+ @no_debug_info_test
+ def test_memory_read_file(self):
+ self.build_run_stop()
+ res = lldb.SBCommandReturnObject()
+ self.ci.HandleCommand("memory read -f d -c 1 `&argc`", res)
+ self.assertTrue(res.Succeeded(), "memory read failed:" + res.GetError())
+
+ # Record golden output.
+ golden_output = res.GetOutput()
+
+ memory_read_file = self.getBuildArtifact("memory-read-output")
+
+ def check_file_content(expected):
+ with open(memory_read_file) as f:
+ lines = f.readlines()
+ lines = [s.strip() for s in lines]
+ expected = [s.strip() for s in expected]
+ self.assertEqual(lines, expected)
+
+ # Sanity check.
+ self.runCmd("memory read -f d -c 1 -o '{}' `&argc`".format(memory_read_file))
+ check_file_content([golden_output])
+
+ # Write some garbage to the file.
+ with open(memory_read_file, 'w') as f:
+ f.write("some garbage")
+
+ # Make sure the file is truncated when we run the command again.
+ self.runCmd("memory read -f d -c 1 -o '{}' `&argc`".format(memory_read_file))
+ check_file_content([golden_output])
+
+ # Make sure the file is appended when we run the command with --append-outfile.
+ self.runCmd(
+ "memory read -f d -c 1 -o '{}' --append-outfile `&argc`".format(
+ memory_read_file))
+ check_file_content([golden_output, golden_output])
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+int main (int argc, char const *argv[])
+{
+ char my_string[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 0};
+ double my_double = 1234.5678;
+ int my_ints[] = {2,4,6,8,10,12,14,16,18,20,22};
+ uint64_t my_uint64s[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ printf("my_string=%s\n", my_string); // Set break point at this line.
+ printf("my_double=%g\n", my_double);
+ return 0;
+}
--- /dev/null
+C_SOURCES := main.c
+
+include Makefile.rules
--- /dev/null
+"""
+Test that "memory region" lookup uses the ABI plugin to remove
+non address bits from addresses before lookup.
+"""
+
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64LinuxTaggedMemoryRegionTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ NO_DEBUG_INFO_TESTCASE = True
+
+ # AArch64 Linux always enables the top byte ignore feature
+ @skipUnlessArch("aarch64")
+ @skipUnlessPlatform(["linux"])
+ def test_mte_regions(self):
+ self.build()
+ self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(self, "main.c",
+ line_number('main.c', '// Set break point at this line.'),
+ num_expected_locations=1)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ if self.process().GetState() == lldb.eStateExited:
+ self.fail("Test program failed to run.")
+
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ # Despite the non address bits we should find a region
+ self.expect("memory region the_page", patterns=[
+ "\[0x[0-9A-Fa-f]+-0x[0-9A-Fa-f]+\) r-x"])
--- /dev/null
+#include <asm/hwcap.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+int main(int argc, char const *argv[]) {
+ void *the_page = mmap(0, sysconf(_SC_PAGESIZE), PROT_READ | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (the_page == MAP_FAILED)
+ return 1;
+
+ // Put something in the top byte (AArch64 Linux always enables top byte
+ // ignore)
+ the_page = (void *)((size_t)the_page | ((size_t)0x34 << 56));
+
+ return 0; // Set break point at this line.
+}
--- /dev/null
+//===- ReleaseModeModelRunner.cpp - Fast, precompiled model runner -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a model runner wrapping an AOT compiled ML model.
+// Only inference is supported.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/Config/config.h"
+#if defined(LLVM_HAVE_TF_AOT)
+
+#include "llvm/Analysis/InlineModelFeatureMaps.h"
+#include "llvm/Analysis/MLInlineAdvisor.h"
+
+// codegen-ed file
+#include "InlinerSizeModel.h" // NOLINT
+
+#include <memory>
+#include <vector>
+
+using namespace llvm;
+namespace {
+
+const char FeedPrefix[] = "feed_";
+const char FetchPrefix[] = "fetch_";
+
+/// MLModelRunner - production mode implementation. It uses a AOT-compiled
+/// SavedModel for efficient execution.
+class ReleaseModeModelRunner final : public MLModelRunner {
+public:
+ ReleaseModeModelRunner(LLVMContext &Ctx);
+ virtual ~ReleaseModeModelRunner() = default;
+
+ bool run() override;
+
+ void setFeature(FeatureIndex Index, int64_t Value) override;
+ int64_t getFeature(int Index) const override;
+
+private:
+ std::vector<int32_t> FeatureIndices;
+ int32_t ResultIndex = -1;
+ std::unique_ptr<llvm::InlinerSizeModel> CompiledModel;
+};
+} // namespace
+
+ReleaseModeModelRunner::ReleaseModeModelRunner(LLVMContext &Ctx)
+ : MLModelRunner(Ctx),
+ CompiledModel(std::make_unique<llvm::InlinerSizeModel>()) {
+ assert(CompiledModel && "The CompiledModel should be valid");
+
+ FeatureIndices.resize(NumberOfFeatures);
+
+ for (size_t I = 0; I < NumberOfFeatures; ++I) {
+ const int Index =
+ CompiledModel->LookupArgIndex(FeedPrefix + FeatureNameMap[I]);
+ assert(Index >= 0 && "Cannot find Feature in inlining model");
+ FeatureIndices[I] = Index;
+ }
+
+ ResultIndex =
+ CompiledModel->LookupResultIndex(std::string(FetchPrefix) + DecisionName);
+ assert(ResultIndex >= 0 && "Cannot find DecisionName in inlining model");
+}
+
+int64_t ReleaseModeModelRunner::getFeature(int Index) const {
+ return *static_cast<int64_t *>(
+ CompiledModel->arg_data(FeatureIndices[Index]));
+}
+
+void ReleaseModeModelRunner::setFeature(FeatureIndex Index, int64_t Value) {
+ *static_cast<int64_t *>(CompiledModel->arg_data(
+ FeatureIndices[static_cast<size_t>(Index)])) = Value;
+}
+
+bool ReleaseModeModelRunner::run() {
+ CompiledModel->Run();
+ return static_cast<bool>(
+ *static_cast<int64_t *>(CompiledModel->result_data(ResultIndex)));
+}
+
+std::unique_ptr<InlineAdvisor>
+llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM) {
+ auto AOTRunner = std::make_unique<ReleaseModeModelRunner>(M.getContext());
+ return std::make_unique<MLInlineAdvisor>(M, MAM, std::move(AOTRunner));
+}
+#endif // defined(LLVM_HAVE_TF_AOT)
SDLoc DL(Op);
SDValue Cond = Op.getOperand(0);
- if (Subtarget->hasScalarCompareEq64() && Op->getOperand(0)->hasOneUse() &&
- !Op->isDivergent()) {
- if (VT == MVT::i64)
- return Op;
- SDValue LHS = DAG.getNode(ISD::BITCAST, DL, MVT::i64, Op.getOperand(1));
- SDValue RHS = DAG.getNode(ISD::BITCAST, DL, MVT::i64, Op.getOperand(2));
- return DAG.getNode(ISD::BITCAST, DL, VT,
- DAG.getSelect(DL, MVT::i64, Cond, LHS, RHS));
- }
-
SDValue Zero = DAG.getConstant(0, DL, MVT::i32);
SDValue One = DAG.getConstant(1, DL, MVT::i32);
continue;
case AMDGPU::S_CSELECT_B32:
- lowerSelect32(Worklist, Inst, MDT);
- Inst.eraseFromParent();
- continue;
case AMDGPU::S_CSELECT_B64:
- splitSelect64(Worklist, Inst, MDT);
+ lowerSelect(Worklist, Inst, MDT);
Inst.eraseFromParent();
continue;
case AMDGPU::S_CMP_EQ_I32:
return std::make_pair(false, nullptr);
}
-void SIInstrInfo::lowerSelect32(SetVectorType &Worklist, MachineInstr &Inst,
- MachineDominatorTree *MDT) const {
+void SIInstrInfo::lowerSelect(SetVectorType &Worklist, MachineInstr &Inst,
+ MachineDominatorTree *MDT) const {
MachineBasicBlock &MBB = *Inst.getParent();
MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo();
addUsersToMoveToVALUWorklist(ResultReg, MRI, Worklist);
}
-void SIInstrInfo::splitSelect64(SetVectorType &Worklist, MachineInstr &Inst,
- MachineDominatorTree *MDT) const {
- // Split S_CSELECT_B64 into a pair of S_CSELECT_B32 and lower them
- // further.
- const DebugLoc &DL = Inst.getDebugLoc();
- MachineBasicBlock::iterator MII = Inst;
- MachineBasicBlock &MBB = *Inst.getParent();
- MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo();
-
- // Get the original operands.
- MachineOperand &Dest = Inst.getOperand(0);
- MachineOperand &Src0 = Inst.getOperand(1);
- MachineOperand &Src1 = Inst.getOperand(2);
- MachineOperand &Cond = Inst.getOperand(3);
-
- Register SCCSource = Cond.getReg();
- bool IsSCC = (SCCSource == AMDGPU::SCC);
-
- // If this is a trivial select where the condition is effectively not SCC
- // (SCCSource is a source of copy to SCC), then the select is semantically
- // equivalent to copying SCCSource. Hence, there is no need to create
- // V_CNDMASK, we can just use that and bail out.
- if (!IsSCC && (Src0.isImm() && Src0.getImm() == -1) &&
- (Src1.isImm() && Src1.getImm() == 0)) {
- MRI.replaceRegWith(Dest.getReg(), SCCSource);
- return;
- }
-
- // Prepare the split destination.
- Register FullDestReg = MRI.createVirtualRegister(&AMDGPU::VReg_64RegClass);
- Register DestSub0 = MRI.createVirtualRegister(&AMDGPU::VGPR_32RegClass);
- Register DestSub1 = MRI.createVirtualRegister(&AMDGPU::VGPR_32RegClass);
-
- // Split the source operands.
- const TargetRegisterClass *Src0RC = nullptr;
- const TargetRegisterClass *Src0SubRC = nullptr;
- if (Src0.isReg()) {
- Src0RC = MRI.getRegClass(Src0.getReg());
- Src0SubRC = RI.getSubRegClass(Src0RC, AMDGPU::sub0);
- }
- const TargetRegisterClass *Src1RC = nullptr;
- const TargetRegisterClass *Src1SubRC = nullptr;
- if (Src1.isReg()) {
- Src1RC = MRI.getRegClass(Src1.getReg());
- Src1SubRC = RI.getSubRegClass(Src1RC, AMDGPU::sub0);
- }
- // Split lo.
- MachineOperand SrcReg0Sub0 =
- buildExtractSubRegOrImm(MII, MRI, Src0, Src0RC, AMDGPU::sub0, Src0SubRC);
- MachineOperand SrcReg1Sub0 =
- buildExtractSubRegOrImm(MII, MRI, Src1, Src1RC, AMDGPU::sub0, Src1SubRC);
- // Split hi.
- MachineOperand SrcReg0Sub1 =
- buildExtractSubRegOrImm(MII, MRI, Src0, Src0RC, AMDGPU::sub1, Src0SubRC);
- MachineOperand SrcReg1Sub1 =
- buildExtractSubRegOrImm(MII, MRI, Src1, Src1RC, AMDGPU::sub1, Src1SubRC);
- // Select the lo part.
- MachineInstr *LoHalf =
- BuildMI(MBB, MII, DL, get(AMDGPU::S_CSELECT_B32), DestSub0)
- .add(SrcReg0Sub0)
- .add(SrcReg1Sub0);
- // Replace the condition operand with the original one.
- LoHalf->getOperand(3).setReg(SCCSource);
- Worklist.insert(LoHalf);
- // Select the hi part.
- MachineInstr *HiHalf =
- BuildMI(MBB, MII, DL, get(AMDGPU::S_CSELECT_B32), DestSub1)
- .add(SrcReg0Sub1)
- .add(SrcReg1Sub1);
- // Replace the condition operand with the original one.
- HiHalf->getOperand(3).setReg(SCCSource);
- Worklist.insert(HiHalf);
- // Merge them back to the original 64-bit one.
- BuildMI(MBB, MII, DL, get(TargetOpcode::REG_SEQUENCE), FullDestReg)
- .addReg(DestSub0)
- .addImm(AMDGPU::sub0)
- .addReg(DestSub1)
- .addImm(AMDGPU::sub1);
- MRI.replaceRegWith(Dest.getReg(), FullDestReg);
-
- // Try to legalize the operands in case we need to swap the order to keep
- // it valid.
- legalizeOperands(*LoHalf, MDT);
- legalizeOperands(*HiHalf, MDT);
-
- // Move all users of this moved value.
- addUsersToMoveToVALUWorklist(FullDestReg, MRI, Worklist);
-}
-
void SIInstrInfo::lowerScalarAbs(SetVectorType &Worklist,
MachineInstr &Inst) const {
MachineBasicBlock &MBB = *Inst.getParent();
moveScalarAddSub(SetVectorType &Worklist, MachineInstr &Inst,
MachineDominatorTree *MDT = nullptr) const;
- void lowerSelect32(SetVectorType &Worklist, MachineInstr &Inst,
- MachineDominatorTree *MDT = nullptr) const;
-
- void splitSelect64(SetVectorType &Worklist, MachineInstr &Inst,
- MachineDominatorTree *MDT = nullptr) const;
+ void lowerSelect(SetVectorType &Worklist, MachineInstr &Inst,
+ MachineDominatorTree *MDT = nullptr) const;
void lowerScalarAbs(SetVectorType &Worklist,
MachineInstr &Inst) const;
def S_CSELECT_B32 : SOP2_32 <"s_cselect_b32",
[(set i32:$sdst, (SelectPat<select> i32:$src0, i32:$src1))]
>;
- def S_CSELECT_B64 : SOP2_64 <"s_cselect_b64",
- [(set i64:$sdst, (SelectPat<select> i64:$src0, i64:$src1))]
- >;
}
+
+ def S_CSELECT_B64 : SOP2_64 <"s_cselect_b64">;
} // End Uses = [SCC]
let Defs = [SCC] in {
--- /dev/null
+//===-- M68kCallLowering.cpp - Call lowering -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file implements the lowering of LLVM calls to machine code calls for
+/// GlobalISel.
+//
+//===----------------------------------------------------------------------===//
+
+#include "M68kCallLowering.h"
+#include "M68kISelLowering.h"
+#include "M68kInstrInfo.h"
+#include "M68kSubtarget.h"
+#include "M68kTargetMachine.h"
+#include "llvm/CodeGen/CallingConvLower.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/TargetCallingConv.h"
+
+using namespace llvm;
+
+M68kCallLowering::M68kCallLowering(const M68kTargetLowering &TLI)
+ : CallLowering(&TLI) {}
+
+struct OutgoingArgHandler : public CallLowering::OutgoingValueHandler {
+ OutgoingArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
+ MachineInstrBuilder MIB)
+ : OutgoingValueHandler(MIRBuilder, MRI), MIB(MIB) {}
+
+ void assignValueToReg(Register ValVReg, Register PhysReg,
+ CCValAssign VA) override {
+ MIB.addUse(PhysReg, RegState::Implicit);
+ Register ExtReg = extendRegister(ValVReg, VA);
+ MIRBuilder.buildCopy(PhysReg, ExtReg);
+ }
+
+ void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
+ MachinePointerInfo &MPO, CCValAssign &VA) override {
+ llvm_unreachable("unimplemented");
+ }
+
+ Register getStackAddress(uint64_t Size, int64_t Offset,
+ MachinePointerInfo &MPO,
+ ISD::ArgFlagsTy Flags) override {
+ llvm_unreachable("unimplemented");
+ }
+
+ MachineInstrBuilder MIB;
+};
+bool M68kCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
+ const Value *Val, ArrayRef<Register> VRegs,
+ FunctionLoweringInfo &FLI,
+ Register SwiftErrorVReg) const {
+
+ auto MIB = MIRBuilder.buildInstrNoInsert(M68k::RTS);
+ bool Success = true;
+ MachineFunction &MF = MIRBuilder.getMF();
+ const Function &F = MF.getFunction();
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ const M68kTargetLowering &TLI = *getTLI<M68kTargetLowering>();
+ CCAssignFn *AssignFn =
+ TLI.getCCAssignFn(F.getCallingConv(), true, F.isVarArg());
+ auto &DL = F.getParent()->getDataLayout();
+ if (!VRegs.empty()) {
+ SmallVector<ArgInfo, 8> SplitArgs;
+ ArgInfo OrigArg{VRegs, Val->getType(), 0};
+ setArgFlags(OrigArg, AttributeList::ReturnIndex, DL, F);
+ splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv());
+ OutgoingValueAssigner ArgAssigner(AssignFn);
+ OutgoingArgHandler ArgHandler(MIRBuilder, MRI, MIB);
+ Success = determineAndHandleAssignments(ArgHandler, ArgAssigner, SplitArgs,
+ MIRBuilder, F.getCallingConv(),
+ F.isVarArg());
+ }
+ MIRBuilder.insertInstr(MIB);
+ return Success;
+}
+
+bool M68kCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
+ const Function &F,
+ ArrayRef<ArrayRef<Register>> VRegs,
+ FunctionLoweringInfo &FLI) const {
+ MachineFunction &MF = MIRBuilder.getMF();
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ const auto &DL = F.getParent()->getDataLayout();
+ auto &TLI = *getTLI<M68kTargetLowering>();
+
+ SmallVector<ArgInfo, 8> SplitArgs;
+ unsigned I = 0;
+ for (const auto &Arg : F.args()) {
+ ArgInfo OrigArg{VRegs[I], Arg.getType(), I};
+ setArgFlags(OrigArg, I + AttributeList::FirstArgIndex, DL, F);
+ splitToValueTypes(OrigArg, SplitArgs, DL, F.getCallingConv());
+ ++I;
+ }
+
+ CCAssignFn *AssignFn =
+ TLI.getCCAssignFn(F.getCallingConv(), false, F.isVarArg());
+ IncomingValueAssigner ArgAssigner(AssignFn);
+ FormalArgHandler ArgHandler(MIRBuilder, MRI);
+ return determineAndHandleAssignments(ArgHandler, ArgAssigner, SplitArgs,
+ MIRBuilder, F.getCallingConv(),
+ F.isVarArg());
+}
+
+void M68kIncomingValueHandler::assignValueToReg(Register ValVReg,
+ Register PhysReg,
+ CCValAssign VA) {
+ MIRBuilder.getMRI()->addLiveIn(PhysReg);
+ MIRBuilder.getMBB().addLiveIn(PhysReg);
+ IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA);
+}
+
+void M68kIncomingValueHandler::assignValueToAddress(Register ValVReg,
+ Register Addr,
+ LLT MemTy,
+ MachinePointerInfo &MPO,
+ CCValAssign &VA) {
+ MachineFunction &MF = MIRBuilder.getMF();
+ auto *MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOLoad, MemTy,
+ inferAlignFromPtrInfo(MF, MPO));
+ MIRBuilder.buildLoad(ValVReg, Addr, *MMO);
+}
+
+Register M68kIncomingValueHandler::getStackAddress(uint64_t Size,
+ int64_t Offset,
+ MachinePointerInfo &MPO,
+ ISD::ArgFlagsTy Flags) {
+ auto &MFI = MIRBuilder.getMF().getFrameInfo();
+ const bool IsImmutable = !Flags.isByVal();
+ int FI = MFI.CreateFixedObject(Size, Offset, IsImmutable);
+ MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI);
+
+ // Build Frame Index
+ llvm::LLT FramePtr = LLT::pointer(
+ 0, MIRBuilder.getMF().getDataLayout().getPointerSizeInBits());
+ MachineInstrBuilder AddrReg = MIRBuilder.buildFrameIndex(FramePtr, FI);
+ StackUsed = std::max(StackUsed, Size + Offset);
+ return AddrReg.getReg(0);
+}
+
+bool M68kCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
+ CallLoweringInfo &Info) const {
+ return false;
+}
+
+bool M68kCallLowering::enableBigEndian() const { return true; }
--- /dev/null
+//===-- M68kCallLowering.h - Call lowering -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file implements the lowering of LLVM calls to machine code calls for
+/// GlobalISel.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_M68K_GLSEL_M68KCALLLOWERING_H
+#define LLVM_LIB_TARGET_M68K_GLSEL_M68KCALLLOWERING_H
+
+#include "llvm/CodeGen/CallingConvLower.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/CodeGen/ValueTypes.h"
+
+namespace llvm {
+
+class M68kTargetLowering;
+
+class M68kCallLowering : public CallLowering {
+ // TODO: We are only supporting return instruction with no value at this time
+ // point
+
+public:
+ M68kCallLowering(const M68kTargetLowering &TLI);
+
+ bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
+ ArrayRef<Register> VRegs, FunctionLoweringInfo &FLI,
+ Register SwiftErrorVReg) const override;
+
+ bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
+ ArrayRef<ArrayRef<Register>> VRegs,
+ FunctionLoweringInfo &FLI) const override;
+
+ bool lowerCall(MachineIRBuilder &MIRBuilder,
+ CallLoweringInfo &Info) const override;
+
+ bool enableBigEndian() const override;
+};
+struct M68kIncomingValueHandler : public CallLowering::IncomingValueHandler {
+ M68kIncomingValueHandler(MachineIRBuilder &MIRBuilder,
+ MachineRegisterInfo &MRI)
+ : CallLowering::IncomingValueHandler(MIRBuilder, MRI) {}
+
+ uint64_t StackUsed;
+
+private:
+ void assignValueToReg(Register ValVReg, Register PhysReg,
+ CCValAssign VA) override;
+
+ void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
+ MachinePointerInfo &MPO, CCValAssign &VA) override;
+
+ Register getStackAddress(uint64_t Size, int64_t Offset,
+ MachinePointerInfo &MPO,
+ ISD::ArgFlagsTy Flags) override;
+};
+
+struct FormalArgHandler : public M68kIncomingValueHandler {
+ FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
+ : M68kIncomingValueHandler(MIRBuilder, MRI) {}
+};
+
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_M68K_GLSEL_M68KCALLLOWERING_H
--- /dev/null
+//===- M68kInstructionSelector.cpp ------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the targeting of the InstructionSelector class for
+/// M68k.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#include "M68kRegisterBankInfo.h"
+#include "M68kSubtarget.h"
+#include "M68kTargetMachine.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "m68k-isel"
+
+using namespace llvm;
+
+#define GET_GLOBALISEL_PREDICATE_BITSET
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATE_BITSET
+
+namespace {
+
+class M68kInstructionSelector : public InstructionSelector {
+public:
+ M68kInstructionSelector(const M68kTargetMachine &TM, const M68kSubtarget &STI,
+ const M68kRegisterBankInfo &RBI);
+
+ bool select(MachineInstr &I) override;
+ static const char *getName() { return DEBUG_TYPE; }
+
+private:
+ bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
+
+ const M68kTargetMachine &TM;
+ const M68kInstrInfo &TII;
+ const M68kRegisterInfo &TRI;
+ const M68kRegisterBankInfo &RBI;
+
+#define GET_GLOBALISEL_PREDICATES_DECL
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATES_DECL
+
+#define GET_GLOBALISEL_TEMPORARIES_DECL
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_TEMPORARIES_DECL
+};
+
+} // end anonymous namespace
+
+#define GET_GLOBALISEL_IMPL
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_IMPL
+
+M68kInstructionSelector::M68kInstructionSelector(
+ const M68kTargetMachine &TM, const M68kSubtarget &STI,
+ const M68kRegisterBankInfo &RBI)
+ : InstructionSelector(), TM(TM), TII(*STI.getInstrInfo()),
+ TRI(*STI.getRegisterInfo()), RBI(RBI),
+
+#define GET_GLOBALISEL_PREDICATES_INIT
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_PREDICATES_INIT
+#define GET_GLOBALISEL_TEMPORARIES_INIT
+#include "M68kGenGlobalISel.inc"
+#undef GET_GLOBALISEL_TEMPORARIES_INIT
+{
+}
+
+bool M68kInstructionSelector::select(MachineInstr &I) {
+ // Certain non-generic instructions also need some special handling.
+ if (!isPreISelGenericOpcode(I.getOpcode()))
+ return true;
+
+ if (selectImpl(I, *CoverageInfo))
+ return true;
+
+ return false;
+}
+
+namespace llvm {
+InstructionSelector *
+createM68kInstructionSelector(const M68kTargetMachine &TM,
+ const M68kSubtarget &Subtarget,
+ const M68kRegisterBankInfo &RBI) {
+ return new M68kInstructionSelector(TM, Subtarget, RBI);
+}
+} // end namespace llvm
--- /dev/null
+//===-- M68kLegalizerInfo.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the targeting of the Machinelegalizer class for M68k.
+//===----------------------------------------------------------------------===//
+
+#include "M68kLegalizerInfo.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/CodeGen/ValueTypes.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Type.h"
+
+using namespace llvm;
+
+M68kLegalizerInfo::M68kLegalizerInfo(const M68kSubtarget &ST) {
+ using namespace TargetOpcode;
+ const LLT S32 = LLT::scalar(32);
+ const LLT P0 = LLT::pointer(0, 32);
+ getActionDefinitionsBuilder(G_LOAD).legalFor({S32});
+ getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({P0});
+ getActionDefinitionsBuilder(G_ADD).legalFor({S32});
+ getActionDefinitionsBuilder(G_SUB).legalFor({S32});
+ getActionDefinitionsBuilder(G_MUL).legalFor({S32});
+ getActionDefinitionsBuilder(G_UDIV).legalFor({S32});
+ getLegacyLegalizerInfo().computeTables();
+}
--- /dev/null
+//===- M68kLegalizerInfo --------------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file declares the targeting of the MachineLegalizer class for
+/// M68k.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_M68K_GLSEL_M68KLEGALIZERINFO_H
+#define LLVM_LIB_TARGET_M68K_GLSEL_M68KLEGALIZERINFO_H
+
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+
+namespace llvm {
+
+class M68kSubtarget;
+
+/// This struct provides the information for the target register banks.
+struct M68kLegalizerInfo : public LegalizerInfo {
+public:
+ M68kLegalizerInfo(const M68kSubtarget &ST);
+};
+} // end namespace llvm
+#endif // LLVM_LIB_TARGET_M68K_GLSEL_M68KLEGALIZERINFO_H
--- /dev/null
+//===-- M68kRegisterBankInfo.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements the targeting of the RegisterBankInfo class for M68k.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#include "M68kRegisterBankInfo.h"
+#include "M68kInstrInfo.h" // For the register classes
+#include "M68kSubtarget.h"
+#include "llvm/CodeGen/GlobalISel/RegisterBank.h"
+#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+
+#define GET_TARGET_REGBANK_IMPL
+#include "M68kGenRegisterBank.inc"
+
+using namespace llvm;
+
+// FIXME: TableGen this.
+// If it grows too much and TableGen still isn't ready to do the job, extract it
+// into an M68kGenRegisterBankInfo.def (similar to AArch64).
+namespace llvm {
+namespace M68k {
+enum PartialMappingIdx {
+ PMI_GPR,
+ PMI_Min = PMI_GPR,
+};
+
+RegisterBankInfo::PartialMapping PartMappings[]{
+ // GPR Partial Mapping
+ {0, 32, GPRRegBank},
+};
+
+enum ValueMappingIdx {
+ InvalidIdx = 0,
+ GPR3OpsIdx = 1,
+};
+
+RegisterBankInfo::ValueMapping ValueMappings[] = {
+ // invalid
+ {nullptr, 0},
+ // 3 operands in GPRs
+ {&PartMappings[PMI_GPR - PMI_Min], 1},
+ {&PartMappings[PMI_GPR - PMI_Min], 1},
+ {&PartMappings[PMI_GPR - PMI_Min], 1},
+
+};
+} // end namespace M68k
+} // end namespace llvm
+
+M68kRegisterBankInfo::M68kRegisterBankInfo(const TargetRegisterInfo &TRI)
+ : M68kGenRegisterBankInfo() {}
+
+const RegisterBank &
+M68kRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC,
+ LLT) const {
+ return getRegBank(M68k::GPRRegBankID);
+}
+
+const RegisterBankInfo::InstructionMapping &
+M68kRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
+ auto Opc = MI.getOpcode();
+
+ if (!isPreISelGenericOpcode(Opc)) {
+ const InstructionMapping &Mapping = getInstrMappingImpl(MI);
+ if (Mapping.isValid())
+ return Mapping;
+ }
+
+ using namespace TargetOpcode;
+
+ unsigned NumOperands = MI.getNumOperands();
+ const ValueMapping *OperandsMapping = &M68k::ValueMappings[M68k::GPR3OpsIdx];
+
+ switch (Opc) {
+ case G_ADD:
+ case G_SUB:
+ case G_MUL:
+ case G_SDIV:
+ case G_UDIV:
+ case G_LOAD:
+ case G_STORE: {
+ OperandsMapping = &M68k::ValueMappings[M68k::GPR3OpsIdx];
+ break;
+ }
+
+ case G_CONSTANT:
+ case G_FRAME_INDEX:
+ OperandsMapping =
+ getOperandsMapping({&M68k::ValueMappings[M68k::GPR3OpsIdx], nullptr});
+ break;
+ default:
+ return getInvalidInstructionMapping();
+ }
+
+ return getInstructionMapping(DefaultMappingID, /*Cost=*/1, OperandsMapping,
+ NumOperands);
+}
--- /dev/null
+//===-- M68kRegisterBankInfo.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file declares the targeting of the RegisterBankInfo class for M68k.
+/// \todo This should be generated by TableGen.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_M68K_GLSEL_M68KREGISTERBANKINFO_H
+#define LLVM_LIB_TARGET_M68K_GLSEL_M68KREGISTERBANKINFO_H
+
+#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
+
+#define GET_REGBANK_DECLARATIONS
+#include "M68kGenRegisterBank.inc"
+#undef GET_REGBANK_DECLARATIONS
+
+namespace llvm {
+
+class TargetRegisterInfo;
+
+class M68kGenRegisterBankInfo : public RegisterBankInfo {
+protected:
+#define GET_TARGET_REGBANK_CLASS
+#include "M68kGenRegisterBank.inc"
+#undef GET_TARGET_REGBANK_CLASS
+};
+
+/// This class provides the information for the target register banks.
+class M68kRegisterBankInfo final : public M68kGenRegisterBankInfo {
+public:
+ M68kRegisterBankInfo(const TargetRegisterInfo &TRI);
+
+ const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC,
+ LLT) const override;
+
+ const InstructionMapping &
+ getInstrMapping(const MachineInstr &MI) const override;
+};
+} // end namespace llvm
+#endif
--- /dev/null
+//===-- M68kRegisterBanks.td - Describe the M68k Banks -------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Define the M68k register banks used for GlobalISel.
+///
+//===----------------------------------------------------------------------===//
+
+/// General Purpose Registers. Here we define a register bank with name AnyGPR
+def GPRRegBank : RegisterBank<"AnyGPR", [XR32]>;
--- /dev/null
+; RUN: opt -S -basic-aa -gvn < %s | FileCheck %s
+; PR10872
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
+target triple = "x86_64-apple-macosx10.7"
+
+@z = internal global i32 0, align 4
+@y = internal global i32 0, align 4
+@x = internal constant i32 0, align 4
+
+; CHECK: @test
+define i32 @test() nounwind uwtable ssp {
+entry:
+ store i32 1, i32* @z
+ tail call void @memset_pattern16(i8* bitcast (i32* @y to i8*), i8* bitcast (i32* @x to i8*), i64 4) nounwind
+; CHECK-NOT: load
+ %l = load i32, i32* @z
+; CHECK: ret i32 1
+ ret i32 %l
+}
+
+declare void @memset_pattern16(i8*, i8* readonly, i64) argmemonly
--- /dev/null
+; RUN: opt -loop-vectorize -enable-interleaved-mem-accesses -prefer-predicate-over-epilogue=predicate-dont-vectorize -S -mcpu=skx --debug-only=loop-vectorize < %s 2>&1 | FileCheck %s --check-prefix=DISABLED_MASKED_STRIDED
+; RUN: opt -loop-vectorize -enable-interleaved-mem-accesses -enable-masked-interleaved-mem-accesses -prefer-predicate-over-epilogue=predicate-dont-vectorize -S -mcpu=skx --debug-only=loop-vectorize < %s 2>&1 | FileCheck %s --check-prefix=ENABLED_MASKED_STRIDED
+; REQUIRES: asserts
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; (1) Interleave-group with factor 4, storing only 2 members out of the 4.
+; Check that when we allow masked-memops to support interleave-group with gaps,
+; the store is vectorized using a wide masked store, with a 1,1,0,0,1,1,0,0,... mask.
+; Check that when we don't allow masked-memops to support interleave-group with gaps,
+; the store is scalarized.
+; The input IR was generated from this source:
+; for(i=0;i<1024;i++){
+; points[i*4] = x[i];
+; points[i*4 + 1] = y[i];
+; }
+; (relates to the testcase in PR50566)
+
+; DISABLED_MASKED_STRIDED: LV: Checking a loop in "test1"
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 26 for VF 2 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 26 for VF 2 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 54 for VF 4 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 54 for VF 4 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 110 for VF 8 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 110 for VF 8 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 228 for VF 16 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 228 for VF 16 For instruction: store i16 %2, i16* %arrayidx7, align 2
+
+; ENABLED_MASKED_STRIDED: LV: Checking a loop in "test1"
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 2 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 15 for VF 2 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 4 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 21 for VF 4 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 8 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 36 for VF 8 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 16 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 73 for VF 16 For instruction: store i16 %2, i16* %arrayidx7, align 2
+
+define void @test1(i16* noalias nocapture %points, i16* noalias nocapture readonly %x, i16* noalias nocapture readonly %y) {
+entry:
+ br label %for.body
+
+for.body:
+ %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]
+ %arrayidx = getelementptr inbounds i16, i16* %x, i64 %indvars.iv
+ %0 = load i16, i16* %arrayidx, align 2
+ %1 = shl nuw nsw i64 %indvars.iv, 2
+ %arrayidx2 = getelementptr inbounds i16, i16* %points, i64 %1
+ store i16 %0, i16* %arrayidx2, align 2
+ %arrayidx4 = getelementptr inbounds i16, i16* %y, i64 %indvars.iv
+ %2 = load i16, i16* %arrayidx4, align 2
+ %3 = or i64 %1, 1
+ %arrayidx7 = getelementptr inbounds i16, i16* %points, i64 %3
+ store i16 %2, i16* %arrayidx7, align 2
+ %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+ %exitcond.not = icmp eq i64 %indvars.iv.next, 1024
+ br i1 %exitcond.not, label %for.end, label %for.body
+
+for.end:
+ ret void
+}
+
+; (2) Same as above, but this time the gaps mask of the store is also And-ed with the
+; fold-tail mask. If using masked memops to vectorize interleaved-group with gaps is
+; not allowed, the store is scalarized and predicated.
+; The input IR was generated from this source:
+; for(i=0;i<numPoints;i++){
+; points[i*4] = x[i];
+; points[i*4 + 1] = y[i];
+; }
+
+; DISABLED_MASKED_STRIDED: LV: Checking a loop in "test2"
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 15 for VF 2 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 3000000 for VF 2 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 31 for VF 4 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 3000000 for VF 4 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 63 for VF 8 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 3000000 for VF 8 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 130 for VF 16 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 3000000 for VF 16 For instruction: store i16 %2, i16* %arrayidx7, align 2
+
+; ENABLED_MASKED_STRIDED: LV: Checking a loop in "test2"
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 2 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 15 for VF 2 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 4 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 21 for VF 4 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 8 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 36 for VF 8 For instruction: store i16 %2, i16* %arrayidx7, align 2
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 0 for VF 16 For instruction: store i16 %0, i16* %arrayidx2, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 73 for VF 16 For instruction: store i16 %2, i16* %arrayidx7, align 2
+
+define void @test2(i16* noalias nocapture %points, i32 %numPoints, i16* noalias nocapture readonly %x, i16* noalias nocapture readonly %y) {
+entry:
+ %cmp15 = icmp sgt i32 %numPoints, 0
+ br i1 %cmp15, label %for.body.preheader, label %for.end
+
+for.body.preheader:
+ %wide.trip.count = zext i32 %numPoints to i64
+ br label %for.body
+
+for.body:
+ %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ %indvars.iv.next, %for.body ]
+ %arrayidx = getelementptr inbounds i16, i16* %x, i64 %indvars.iv
+ %0 = load i16, i16* %arrayidx, align 2
+ %1 = shl nsw i64 %indvars.iv, 2
+ %arrayidx2 = getelementptr inbounds i16, i16* %points, i64 %1
+ store i16 %0, i16* %arrayidx2, align 2
+ %arrayidx4 = getelementptr inbounds i16, i16* %y, i64 %indvars.iv
+ %2 = load i16, i16* %arrayidx4, align 2
+ %3 = or i64 %1, 1
+ %arrayidx7 = getelementptr inbounds i16, i16* %points, i64 %3
+ store i16 %2, i16* %arrayidx7, align 2
+ %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+ %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count
+ br i1 %exitcond.not, label %for.end.loopexit, label %for.body
+
+for.end.loopexit:
+ br label %for.end
+
+for.end:
+ ret void
+}
+
+; (3) Testing a scenario of a conditional store. The gaps mask of the store is also
+; And-ed with the condition mask (x[i] > 0).
+; If using masked memops to vectorize interleaved-group with gaps is
+; not allowed, the store is scalarized and predicated.
+; Here the Interleave-group is with factor 3, storing only 1 member out of the 3.
+; The input IR was generated from this source:
+; for(i=0;i<1024;i++){
+; if (x[i] > 0)
+; points[i*3] = x[i];
+; }
+
+; DISABLED_MASKED_STRIDED: LV: Checking a loop in "test"
+;
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 2 for VF 2 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 4 for VF 4 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 8 for VF 8 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; DISABLED_MASKED_STRIDED: LV: Found an estimated cost of 20 for VF 16 For instruction: store i16 %0, i16* %arrayidx6, align 2
+
+; ENABLED_MASKED_STRIDED: LV: Checking a loop in "test"
+;
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 1 for VF 1 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 2 for VF 2 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 4 for VF 4 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 8 for VF 8 For instruction: store i16 %0, i16* %arrayidx6, align 2
+; ENABLED_MASKED_STRIDED: LV: Found an estimated cost of 20 for VF 16 For instruction: store i16 %0, i16* %arrayidx6, align 2
+
+define void @test(i16* noalias nocapture %points, i16* noalias nocapture readonly %x, i16* noalias nocapture readnone %y) {
+entry:
+ br label %for.body
+
+for.body:
+ %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.inc ]
+ %arrayidx = getelementptr inbounds i16, i16* %x, i64 %indvars.iv
+ %0 = load i16, i16* %arrayidx, align 2
+ %cmp1 = icmp sgt i16 %0, 0
+ br i1 %cmp1, label %if.then, label %for.inc
+
+if.then:
+ %1 = mul nuw nsw i64 %indvars.iv, 3
+ %arrayidx6 = getelementptr inbounds i16, i16* %points, i64 %1
+ store i16 %0, i16* %arrayidx6, align 2
+ br label %for.inc
+
+for.inc:
+ %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+ %exitcond.not = icmp eq i64 %indvars.iv.next, 1024
+ br i1 %exitcond.not, label %for.end, label %for.body
+
+for.end:
+ ret void
+}
--- /dev/null
+; RUN: opt -loop-load-elim -mtriple=aarch64--linux-gnu -mattr=+sve < %s
+
+; This regression test is verifying that a GEP instruction performed on a
+; scalable vector does not produce a 'assumption that TypeSize is not scalable'
+; warning in the llvm::getGEPInductionOperand function.
+
+define void @get_gep_induction_operand_typesize_warning(i64 %n, <vscale x 4 x i32>* %a) {
+entry:
+ br label %loop.body
+
+loop.body:
+ %0 = phi i64 [ 0, %entry ], [ %1, %loop.body ]
+ %idx = getelementptr <vscale x 4 x i32>, <vscale x 4 x i32>* %a, i64 %0
+ store <vscale x 4 x i32> zeroinitializer, <vscale x 4 x i32>* %idx
+ %1 = add i64 %0, 1
+ %2 = icmp eq i64 %1, %n
+ br i1 %2, label %loop.end, label %loop.body
+
+loop.end:
+ ret void
+}
; CI-DAG: v_cndmask_b32_e32 v[[LO:[0-9]+]], 0, [[VPTR]]
; HSA-DAG: v_mov_b32_e32 [[K:v[0-9]+]], 7
-; GFX9-DAG: s_load_dword s[[PTR:[0-9]+]], s[4:5], 0x0{{$}}
+; GFX9-DAG: s_load_dword [[PTR:s[0-9]+]], s[4:5], 0x0{{$}}
; GFX9-DAG: s_getreg_b32 [[SSRC_SHARED:s[0-9]+]], hwreg(HW_REG_SH_MEM_BASES, 16, 16)
-; GFX9-DAG: s_lshl_b32 s[[SSRC_SHARED_BASE:[0-9]+]], [[SSRC_SHARED]], 16
+; GFX9-DAG: s_lshl_b32 [[SSRC_SHARED_BASE:s[0-9]+]], [[SSRC_SHARED]], 16
+; GFX9-DAG: v_mov_b32_e32 [[VAPERTURE:v[0-9]+]], [[SSRC_SHARED_BASE]]
; GFX9-XXX: v_mov_b32_e32 [[VAPERTURE:v[0-9]+]], src_shared_base
-; GFX9: s_cmp_lg_u32 s[[PTR]], -1
-; GFX9: s_cselect_b64 s{{\[}}[[SEL_LO:[0-9]+]]:[[SEL_HI:[0-9]+]]{{\]}}, s{{\[}}[[PTR]]:[[SSRC_SHARED_BASE]]{{\]}}, 0
-; GFX9-DAG: v_mov_b32_e32 v[[LO:[0-9]+]], s[[SEL_LO]]
-; GFX9-DAG: v_mov_b32_e32 v[[HI:[0-9]+]], s[[SEL_HI]]
+; GFX9: s_cmp_lg_u32 [[PTR]], -1
+; GFX9: s_cselect_b64 vcc, -1, 0
+; GFX9: v_cndmask_b32_e32 v[[HI:[0-9]+]], 0, [[VAPERTURE]], vcc
+; GFX9-DAG: v_mov_b32_e32 [[VPTR:v[0-9]+]], [[PTR]]
+; GFX9-DAG: v_cndmask_b32_e32 v[[LO:[0-9]+]], 0, [[VPTR]]
; HSA: flat_store_dword v{{\[}}[[LO]]:[[HI]]{{\]}}, [[K]]
; CI-DAG: v_mov_b32_e32 [[VPTR:v[0-9]+]], [[PTR]]
; CI-DAG: v_cndmask_b32_e32 v[[LO:[0-9]+]], 0, [[VPTR]]
-; GFX9-DAG: s_load_dword s[[PTR:[0-9]+]], s[4:5], 0x0{{$}}
+; GFX9-DAG: s_load_dword [[PTR:s[0-9]+]], s[4:5], 0x0{{$}}
; GFX9-DAG: s_getreg_b32 [[SSRC_PRIVATE:s[0-9]+]], hwreg(HW_REG_SH_MEM_BASES, 0, 16)
-; GFX9-DAG: s_lshl_b32 s[[SSRC_PRIVATE_BASE:[0-9]+]], [[SSRC_PRIVATE]], 16
+; GFX9-DAG: s_lshl_b32 [[SSRC_PRIVATE_BASE:s[0-9]+]], [[SSRC_PRIVATE]], 16
+; GFX9-DAG: v_mov_b32_e32 [[VAPERTURE:v[0-9]+]], [[SSRC_PRIVATE_BASE]]
; GFX9-XXX: v_mov_b32_e32 [[VAPERTURE:v[0-9]+]], src_private_base
; GFX9-DAG: v_mov_b32_e32 [[K:v[0-9]+]], 7
-; GFX9: s_cmp_lg_u32 s[[PTR]], -1
-; GFX9: s_cselect_b64 s{{\[}}[[SEL_LO:[0-9]+]]:[[SEL_HI:[0-9]+]]{{\]}}, s{{\[}}[[PTR]]:[[SSRC_PRIVATE_BASE]]{{\]}}, 0
-; GFX9-DAG: v_mov_b32_e32 v[[LO:[0-9]+]], s[[SEL_LO]]
-; GFX9-DAG: v_mov_b32_e32 v[[HI:[0-9]+]], s[[SEL_HI]]
+; GFX9: s_cmp_lg_u32 [[PTR]], -1
+; GFX9: s_cselect_b64 vcc, -1, 0
+; GFX9: v_cndmask_b32_e32 v[[HI:[0-9]+]], 0, [[VAPERTURE]], vcc
+; GFX9: v_mov_b32_e32 [[VPTR:v[0-9]+]], [[PTR]]
+; GFX9-DAG: v_cndmask_b32_e32 v[[LO:[0-9]+]], 0, [[VPTR]]
; HSA: flat_store_dword v{{\[}}[[LO]]:[[HI]]{{\]}}, [[K]]
; RUN: opt -S -mtriple=amdgcn-- -mcpu=tahiti -amdgpu-codegenprepare -amdgpu-bypass-slow-div=0 %s | FileCheck %s
; RUN: llc -mtriple=amdgcn-- -mcpu=tahiti -amdgpu-bypass-slow-div=0 < %s | FileCheck -check-prefix=GFX6 %s
; RUN: llc -mtriple=amdgcn-- -mcpu=gfx900 -amdgpu-bypass-slow-div=0 < %s | FileCheck -check-prefix=GFX9 %s
-; RUN: llc -mtriple=amdgcn-- -mcpu=gfx90a -amdgpu-bypass-slow-div=0 < %s | FileCheck -check-prefix=GFX90A %s
define amdgpu_kernel void @udiv_i32(i32 addrspace(1)* %out, i32 %x, i32 %y) {
; CHECK-LABEL: @udiv_i32(
; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
; GFX9-NEXT: global_store_dword v2, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: udiv_i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_sub_i32 s4, 0, s3
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s4, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v2, s2, v2
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v3, vcc
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = udiv i32 %x, %y
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_dword v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: urem_i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_sub_i32 s4, 0, s3
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s4, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v0, s2, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = urem i32 %x, %y
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v0, s4, v0
; GFX9-NEXT: global_store_dword v2, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: sdiv_i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_add_i32 s3, s3, s4
-; GFX90A-NEXT: s_xor_b32 s3, s3, s4
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_ashr_i32 s5, s2, 31
-; GFX90A-NEXT: s_add_i32 s2, s2, s5
-; GFX90A-NEXT: s_xor_b32 s4, s5, s4
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s2, s2, s5
-; GFX90A-NEXT: s_sub_i32 s5, 0, s3
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s5, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v2, s2, v2
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v3, vcc
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v0, s4, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s4, v0
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = sdiv i32 %x, %y
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_dword v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: srem_i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_add_i32 s3, s3, s4
-; GFX90A-NEXT: s_xor_b32 s3, s3, s4
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_sub_i32 s5, 0, s3
-; GFX90A-NEXT: s_ashr_i32 s4, s2, 31
-; GFX90A-NEXT: s_add_i32 s2, s2, s4
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s2, s2, s4
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s5, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v0, s2, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v0, s4, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s4, v0
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = srem i32 %x, %y
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, 0, v4, vcc
; GFX9-NEXT: global_store_short v3, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: udiv_i16:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dword s2, s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v3, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshr_b32 s3, s2, 16
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_and_b32 s2, s2, 0xffff
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v1, s2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v2, v0
-; GFX90A-NEXT: v_mul_f32_e32 v2, v1, v2
-; GFX90A-NEXT: v_trunc_f32_e32 v2, v2
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v4, v2
-; GFX90A-NEXT: v_mad_f32 v1, -v2, v0, v1
-; GFX90A-NEXT: v_cmp_ge_f32_e64 vcc, |v1|, v0
-; GFX90A-NEXT: v_addc_co_u32_e32 v0, vcc, 0, v4, vcc
-; GFX90A-NEXT: global_store_short v3, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = udiv i16 %x, %y
store i16 %r, i16 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_short v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: urem_i16:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dword s2, s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v3, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshr_b32 s3, s2, 16
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_and_b32 s4, s2, 0xffff
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v1, s4
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v2, v0
-; GFX90A-NEXT: v_mul_f32_e32 v2, v1, v2
-; GFX90A-NEXT: v_trunc_f32_e32 v2, v2
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v4, v2
-; GFX90A-NEXT: v_mad_f32 v1, -v2, v0, v1
-; GFX90A-NEXT: v_cmp_ge_f32_e64 vcc, |v1|, v0
-; GFX90A-NEXT: v_addc_co_u32_e32 v0, vcc, 0, v4, vcc
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v0, s2, v0
-; GFX90A-NEXT: global_store_short v3, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = urem i16 %x, %y
store i16 %r, i16 addrspace(1)* %out
ret void
; GFX9-NEXT: v_add_u32_e32 v0, s0, v3
; GFX9-NEXT: global_store_short v1, v0, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_add_u32_e32 v0, s0, v3
; GFX90A-NEXT: global_store_short v1, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i16 %x, %y
store i16 %r, i16 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_short v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_sub_u32_e32 v0, s4, v0
; GFX90A-NEXT: global_store_short v1, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i16 %x, %y
store i16 %r, i16 addrspace(1)* %out
ret void
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, 0, v4, vcc
; GFX9-NEXT: global_store_byte v2, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: udiv_i8:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dword s2, s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: v_cvt_f32_ubyte1_e32 v0, s2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v0
-; GFX90A-NEXT: v_cvt_f32_ubyte0_e32 v3, s2
-; GFX90A-NEXT: v_mul_f32_e32 v1, v3, v1
-; GFX90A-NEXT: v_trunc_f32_e32 v1, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v4, v1
-; GFX90A-NEXT: v_mad_f32 v1, -v1, v0, v3
-; GFX90A-NEXT: v_cmp_ge_f32_e64 vcc, |v1|, v0
-; GFX90A-NEXT: v_addc_co_u32_e32 v0, vcc, 0, v4, vcc
-; GFX90A-NEXT: global_store_byte v2, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = udiv i8 %x, %y
store i8 %r, i8 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_byte v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i8:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_sub_u32_e32 v0, s4, v0
; GFX90A-NEXT: global_store_byte v2, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem i8 %x, %y
store i8 %r, i8 addrspace(1)* %out
ret void
; GFX9-NEXT: v_add_u32_e32 v0, s0, v3
; GFX9-NEXT: global_store_byte v1, v0, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i8:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_add_u32_e32 v0, s0, v3
; GFX90A-NEXT: global_store_byte v1, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i8 %x, %y
store i8 %r, i8 addrspace(1)* %out
ret void
; GFX9-NEXT: v_sub_u32_e32 v0, s4, v0
; GFX9-NEXT: global_store_byte v1, v0, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i8:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_sub_u32_e32 v1, s4, v1
; GFX90A-NEXT: global_store_byte v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i8 %x, %y
store i8 %r, i8 addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e32 v3, v5, v6, vcc
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v4i32:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v6, vcc
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <4 x i32> %x, %y
store <4 x i32> %r, <4 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e32 v3, v3, v5, vcc
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: urem_v4i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx8 s[4:11], s[0:1], 0x34
-; GFX90A-NEXT: s_mov_b32 s12, 0x4f7ffffe
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v4, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s8
-; GFX90A-NEXT: s_sub_i32 s2, 0, s8
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v1, s9
-; GFX90A-NEXT: s_sub_i32 s3, 0, s9
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_f32_e32 v0, s12, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_f32_e32 v1, s12, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_lo_u32 v2, s2, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s4, v0
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s8
-; GFX90A-NEXT: v_sub_u32_e32 v0, s4, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s8, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s8, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s8, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s8, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v2, s10
-; GFX90A-NEXT: v_mul_lo_u32 v3, s3, v1
-; GFX90A-NEXT: v_mul_hi_u32 v3, v1, v3
-; GFX90A-NEXT: v_add_u32_e32 v1, v1, v3
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v2, v2
-; GFX90A-NEXT: v_mul_hi_u32 v1, s5, v1
-; GFX90A-NEXT: v_mul_lo_u32 v1, v1, s9
-; GFX90A-NEXT: v_sub_u32_e32 v1, s5, v1
-; GFX90A-NEXT: v_mul_f32_e32 v2, s12, v2
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v2, v2
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s9, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s9, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s9, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s9, v1
-; GFX90A-NEXT: s_sub_i32 s2, 0, s10
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_mul_lo_u32 v3, s2, v2
-; GFX90A-NEXT: v_mul_hi_u32 v3, v2, v3
-; GFX90A-NEXT: v_add_u32_e32 v2, v2, v3
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v3, s11
-; GFX90A-NEXT: v_mul_hi_u32 v2, s6, v2
-; GFX90A-NEXT: v_mul_lo_u32 v2, v2, s10
-; GFX90A-NEXT: v_sub_u32_e32 v2, s6, v2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v3, v3
-; GFX90A-NEXT: v_subrev_u32_e32 v5, s10, v2
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s10, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v5, vcc
-; GFX90A-NEXT: v_mul_f32_e32 v3, s12, v3
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v3, v3
-; GFX90A-NEXT: v_subrev_u32_e32 v5, s10, v2
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s10, v2
-; GFX90A-NEXT: s_sub_i32 s2, 0, s11
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v5, vcc
-; GFX90A-NEXT: v_mul_lo_u32 v5, s2, v3
-; GFX90A-NEXT: v_mul_hi_u32 v5, v3, v5
-; GFX90A-NEXT: v_add_u32_e32 v3, v3, v5
-; GFX90A-NEXT: v_mul_hi_u32 v3, s7, v3
-; GFX90A-NEXT: v_mul_lo_u32 v3, v3, s11
-; GFX90A-NEXT: v_sub_u32_e32 v3, s7, v3
-; GFX90A-NEXT: v_subrev_u32_e32 v5, s11, v3
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s11, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v5, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v5, s11, v3
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s11, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v5, vcc
-; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = urem <4 x i32> %x, %y
store <4 x i32> %r, <4 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v3, s2, v3
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: sdiv_v4i32:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx8 s[4:11], s[0:1], 0x34
-; GFX90A-NEXT: s_mov_b32 s13, 0x4f7ffffe
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v4, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_ashr_i32 s2, s8, 31
-; GFX90A-NEXT: s_add_i32 s3, s8, s2
-; GFX90A-NEXT: s_xor_b32 s3, s3, s2
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_ashr_i32 s8, s4, 31
-; GFX90A-NEXT: s_add_i32 s4, s4, s8
-; GFX90A-NEXT: s_xor_b32 s2, s8, s2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s4, s4, s8
-; GFX90A-NEXT: s_sub_i32 s8, 0, s3
-; GFX90A-NEXT: s_ashr_i32 s12, s9, 31
-; GFX90A-NEXT: v_mul_f32_e32 v0, s13, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v1, s8, v0
-; GFX90A-NEXT: v_mul_hi_u32 v1, v0, v1
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v1
-; GFX90A-NEXT: v_mul_hi_u32 v0, s4, v0
-; GFX90A-NEXT: v_mul_lo_u32 v1, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v1, s4, v1
-; GFX90A-NEXT: s_add_i32 s4, s9, s12
-; GFX90A-NEXT: s_xor_b32 s4, s4, s12
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v3, s4
-; GFX90A-NEXT: v_add_u32_e32 v2, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v2, vcc
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v1
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v3
-; GFX90A-NEXT: v_add_u32_e32 v2, 1, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v0, s2, v0
-; GFX90A-NEXT: v_mul_f32_e32 v1, s13, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v1, v1
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s2, v0
-; GFX90A-NEXT: s_ashr_i32 s2, s5, 31
-; GFX90A-NEXT: s_add_i32 s5, s5, s2
-; GFX90A-NEXT: s_xor_b32 s3, s2, s12
-; GFX90A-NEXT: s_xor_b32 s2, s5, s2
-; GFX90A-NEXT: s_sub_i32 s5, 0, s4
-; GFX90A-NEXT: v_mul_lo_u32 v2, s5, v1
-; GFX90A-NEXT: v_mul_hi_u32 v2, v1, v2
-; GFX90A-NEXT: v_add_u32_e32 v1, v1, v2
-; GFX90A-NEXT: v_mul_hi_u32 v1, s2, v1
-; GFX90A-NEXT: v_mul_lo_u32 v2, v1, s4
-; GFX90A-NEXT: v_sub_u32_e32 v2, s2, v2
-; GFX90A-NEXT: s_ashr_i32 s2, s10, 31
-; GFX90A-NEXT: s_add_i32 s5, s10, s2
-; GFX90A-NEXT: s_xor_b32 s5, s5, s2
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v5, s5
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s4, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s4, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v3, vcc
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s4, v2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v2, v5
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v1, s3, v1
-; GFX90A-NEXT: v_mul_f32_e32 v2, s13, v2
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v2, v2
-; GFX90A-NEXT: v_subrev_u32_e32 v1, s3, v1
-; GFX90A-NEXT: s_ashr_i32 s3, s6, 31
-; GFX90A-NEXT: s_add_i32 s4, s6, s3
-; GFX90A-NEXT: s_xor_b32 s2, s3, s2
-; GFX90A-NEXT: s_xor_b32 s3, s4, s3
-; GFX90A-NEXT: s_sub_i32 s4, 0, s5
-; GFX90A-NEXT: v_mul_lo_u32 v3, s4, v2
-; GFX90A-NEXT: v_mul_hi_u32 v3, v2, v3
-; GFX90A-NEXT: v_add_u32_e32 v2, v2, v3
-; GFX90A-NEXT: v_mul_hi_u32 v2, s3, v2
-; GFX90A-NEXT: v_mul_lo_u32 v3, v2, s5
-; GFX90A-NEXT: v_sub_u32_e32 v3, s3, v3
-; GFX90A-NEXT: s_ashr_i32 s3, s11, 31
-; GFX90A-NEXT: s_add_i32 s4, s11, s3
-; GFX90A-NEXT: s_xor_b32 s4, s4, s3
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v6, s4
-; GFX90A-NEXT: v_add_u32_e32 v5, 1, v2
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s5, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v5, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v5, s5, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v5, vcc
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s5, v3
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v3, v6
-; GFX90A-NEXT: v_add_u32_e32 v5, 1, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v2, v5, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v2, s2, v2
-; GFX90A-NEXT: v_mul_f32_e32 v3, s13, v3
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v3, v3
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s2, v2
-; GFX90A-NEXT: s_ashr_i32 s2, s7, 31
-; GFX90A-NEXT: s_add_i32 s5, s7, s2
-; GFX90A-NEXT: s_xor_b32 s3, s2, s3
-; GFX90A-NEXT: s_xor_b32 s2, s5, s2
-; GFX90A-NEXT: s_sub_i32 s5, 0, s4
-; GFX90A-NEXT: v_mul_lo_u32 v5, s5, v3
-; GFX90A-NEXT: v_mul_hi_u32 v5, v3, v5
-; GFX90A-NEXT: v_add_u32_e32 v3, v3, v5
-; GFX90A-NEXT: v_mul_hi_u32 v3, s2, v3
-; GFX90A-NEXT: v_mul_lo_u32 v5, v3, s4
-; GFX90A-NEXT: v_sub_u32_e32 v5, s2, v5
-; GFX90A-NEXT: v_add_u32_e32 v6, 1, v3
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s4, v5
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v6, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v6, s4, v5
-; GFX90A-NEXT: v_cndmask_b32_e32 v5, v5, v6, vcc
-; GFX90A-NEXT: v_add_u32_e32 v6, 1, v3
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s4, v5
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v6, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v3, s3, v3
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s3, v3
-; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = sdiv <4 x i32> %x, %y
store <4 x i32> %r, <4 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v3, s5, v3
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v4i32:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subrev_u32_e32 v3, s2, v3
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <4 x i32> %x, %y
store <4 x i32> %r, <4 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_lshl_or_b32 v0, v3, 16, v0
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v4i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_lshl_or_b32 v0, v3, 16, v0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <4 x i16> %x, %y
store <4 x i16> %r, <4 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: v_lshl_or_b32 v0, v5, 16, v0
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v4i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_lshl_or_b32 v0, v3, 16, v0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem <4 x i16> %x, %y
store <4 x i16> %r, <4 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: v_lshl_or_b32 v0, v4, 16, v0
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v4i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_lshl_or_b32 v0, v4, 16, v0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <4 x i16> %x, %y
store <4 x i16> %r, <4 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: v_lshl_or_b32 v0, v0, 16, v3
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v4i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_lshl_or_b32 v0, v4, 16, v0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <4 x i16> %x, %y
store <4 x i16> %r, <4 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 7, v0
; GFX9-NEXT: global_store_byte v2, v0, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_i3:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 7, v0
; GFX90A-NEXT: global_store_byte v2, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv i3 %x, %y
store i3 %r, i3 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_byte v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i3:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v1, 7, v1
; GFX90A-NEXT: global_store_byte v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem i3 %x, %y
store i3 %r, i3 addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 7, v0
; GFX9-NEXT: global_store_byte v1, v0, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i3:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 7, v0
; GFX90A-NEXT: global_store_byte v1, v0, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i3 %x, %y
store i3 %r, i3 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_byte v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i3:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v1, 7, v1
; GFX90A-NEXT: global_store_byte v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i3 %x, %y
store i3 %r, i3 addrspace(1)* %out
ret void
; GFX9-NEXT: global_store_short v1, v3, s[4:5] offset:4
; GFX9-NEXT: global_store_dword v1, v0, s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v3i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: global_store_short v1, v3, s[4:5] offset:4
; GFX90A-NEXT: global_store_dword v1, v0, s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <3 x i16> %x, %y
store <3 x i16> %r, <3 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: global_store_short v3, v2, s[6:7] offset:4
; GFX9-NEXT: global_store_dword v3, v0, s[6:7]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v3i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: global_store_short v1, v3, s[4:5] offset:4
; GFX90A-NEXT: global_store_dword v1, v0, s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem <3 x i16> %x, %y
store <3 x i16> %r, <3 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: global_store_short v1, v0, s[6:7] offset:4
; GFX9-NEXT: global_store_dword v1, v2, s[6:7]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v3i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: global_store_short v1, v0, s[6:7] offset:4
; GFX90A-NEXT: global_store_dword v1, v2, s[6:7]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <3 x i16> %x, %y
store <3 x i16> %r, <3 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: global_store_short v3, v2, s[0:1] offset:4
; GFX9-NEXT: global_store_dword v3, v0, s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v3i16:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: global_store_short v1, v3, s[6:7] offset:4
; GFX90A-NEXT: global_store_dword v1, v0, s[6:7]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <3 x i16> %x, %y
store <3 x i16> %r, <3 x i16> addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX9-NEXT: global_store_short v2, v0, s[4:5] offset:4
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v3i15:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX90A-NEXT: global_store_short v2, v0, s[4:5] offset:4
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <3 x i15> %x, %y
store <3 x i15> %r, <3 x i15> addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX9-NEXT: global_store_short v2, v0, s[4:5] offset:4
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v3i15:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX90A-NEXT: global_store_short v2, v0, s[4:5] offset:4
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem <3 x i15> %x, %y
store <3 x i15> %r, <3 x i15> addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX9-NEXT: global_store_short v2, v0, s[6:7] offset:4
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v3i15:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX90A-NEXT: global_store_short v2, v0, s[6:7] offset:4
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <3 x i15> %x, %y
store <3 x i15> %r, <3 x i15> addrspace(1)* %out
ret void
; GFX9-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX9-NEXT: global_store_short v4, v0, s[4:5] offset:4
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v3i15:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_and_b32_e32 v0, 0x1fff, v1
; GFX90A-NEXT: global_store_short v2, v0, s[4:5] offset:4
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <3 x i15> %x, %y
store <3 x i15> %r, <3 x i15> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_i32_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv i32 %x, 1235195
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv i32 %x, 4096
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_i32_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i32 4096, %y
%r = udiv i32 %x, %shl.y
store i32 %r, i32 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v2i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <2 x i32> %x, <i32 4096, i32 4096>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v2i32_mixed_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <2 x i32> %x, <i32 4096, i32 4095>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: udiv_v2i32_pow2_shl_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x34
-; GFX90A-NEXT: s_movk_i32 s8, 0x1000
-; GFX90A-NEXT: s_mov_b32 s9, 0x4f7ffffe
-; GFX90A-NEXT: s_load_dwordx2 s[4:5], s[0:1], 0x24
-; GFX90A-NEXT: s_load_dwordx2 s[6:7], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshl_b32 s2, s8, s2
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s2
-; GFX90A-NEXT: s_lshl_b32 s0, s8, s3
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v1, s0
-; GFX90A-NEXT: s_sub_i32 s1, 0, s2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_f32_e32 v0, s9, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_f32_e32 v1, s9, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_lo_u32 v3, s1, v0
-; GFX90A-NEXT: v_mul_hi_u32 v3, v0, v3
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v3
-; GFX90A-NEXT: v_mul_hi_u32 v0, s6, v0
-; GFX90A-NEXT: v_mul_lo_u32 v3, v0, s2
-; GFX90A-NEXT: v_sub_u32_e32 v3, s6, v3
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v4, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v4, s2, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v4, vcc
-; GFX90A-NEXT: s_sub_i32 s1, 0, s0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v3
-; GFX90A-NEXT: v_mul_lo_u32 v3, s1, v1
-; GFX90A-NEXT: v_mul_hi_u32 v3, v1, v3
-; GFX90A-NEXT: v_add_u32_e32 v1, v1, v3
-; GFX90A-NEXT: v_mul_hi_u32 v1, s7, v1
-; GFX90A-NEXT: v_mul_lo_u32 v3, v1, s0
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v0
-; GFX90A-NEXT: v_sub_u32_e32 v3, s7, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v4, vcc
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v4, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v4, vcc
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
-; GFX90A-NEXT: s_endpgm
%shl.y = shl <2 x i32> <i32 4096, i32 4096>, %y
%r = udiv <2 x i32> %x, %shl.y
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i32_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem i32 %x, 1235195
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem i32 %x, 4096
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i32_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i32 4096, %y
%r = urem i32 %x, %shl.y
store i32 %r, i32 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v2i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem <2 x i32> %x, <i32 4096, i32 4096>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: urem_v2i32_pow2_shl_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x34
-; GFX90A-NEXT: s_movk_i32 s8, 0x1000
-; GFX90A-NEXT: s_load_dwordx2 s[4:5], s[0:1], 0x24
-; GFX90A-NEXT: s_load_dwordx2 s[6:7], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshl_b32 s2, s8, s2
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s2
-; GFX90A-NEXT: s_lshl_b32 s0, s8, s3
-; GFX90A-NEXT: s_mov_b32 s3, 0x4f7ffffe
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v1, s0
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_sub_i32 s1, 0, s2
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_f32_e32 v0, s3, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_f32_e32 v1, s3, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v1, v1
-; GFX90A-NEXT: v_mul_lo_u32 v3, s1, v0
-; GFX90A-NEXT: v_mul_hi_u32 v3, v0, v3
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v3
-; GFX90A-NEXT: v_mul_hi_u32 v0, s6, v0
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s2
-; GFX90A-NEXT: v_sub_u32_e32 v0, s6, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s2, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s2, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v0
-; GFX90A-NEXT: s_sub_i32 s1, 0, s0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_mul_lo_u32 v3, s1, v1
-; GFX90A-NEXT: v_mul_hi_u32 v3, v1, v3
-; GFX90A-NEXT: v_add_u32_e32 v1, v1, v3
-; GFX90A-NEXT: v_mul_hi_u32 v1, s7, v1
-; GFX90A-NEXT: v_mul_lo_u32 v1, v1, s0
-; GFX90A-NEXT: v_sub_u32_e32 v1, s7, v1
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s0, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s0, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
-; GFX90A-NEXT: s_endpgm
%shl.y = shl <2 x i32> <i32 4096, i32 4096>, %y
%r = urem <2 x i32> %x, %shl.y
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i32_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i32 %x, 1235195
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i32 %x, 4096
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v0, s2, v0
; GFX9-NEXT: global_store_dword v2, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: sdiv_i32_pow2_shl_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshl_b32 s3, 0x1000, s3
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_add_i32 s3, s3, s4
-; GFX90A-NEXT: s_xor_b32 s3, s3, s4
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_sub_i32 s6, 0, s3
-; GFX90A-NEXT: s_ashr_i32 s5, s2, 31
-; GFX90A-NEXT: s_add_i32 s2, s2, s5
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s2, s2, s5
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s6, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v3, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v3, s2, v3
-; GFX90A-NEXT: v_add_u32_e32 v2, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v2, v3, v2, vcc
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v2
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v4, vcc
-; GFX90A-NEXT: s_xor_b32 s2, s5, s4
-; GFX90A-NEXT: v_xor_b32_e32 v0, s2, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s2, v0
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%shl.y = shl i32 4096, %y
%r = sdiv i32 %x, %shl.y
store i32 %r, i32 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v2i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <2 x i32> %x, <i32 4096, i32 4096>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: ssdiv_v2i32_mixed_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <2 x i32> %x, <i32 4096, i32 4095>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v1, s1, v1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: sdiv_v2i32_pow2_shl_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x34
-; GFX90A-NEXT: s_movk_i32 s8, 0x1000
-; GFX90A-NEXT: s_load_dwordx2 s[4:5], s[0:1], 0x24
-; GFX90A-NEXT: s_load_dwordx2 s[6:7], s[0:1], 0x2c
-; GFX90A-NEXT: s_mov_b32 s10, 0x4f7ffffe
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshl_b32 s2, s8, s2
-; GFX90A-NEXT: s_ashr_i32 s9, s2, 31
-; GFX90A-NEXT: s_add_i32 s2, s2, s9
-; GFX90A-NEXT: s_xor_b32 s2, s2, s9
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s2
-; GFX90A-NEXT: s_ashr_i32 s1, s6, 31
-; GFX90A-NEXT: s_lshl_b32 s0, s8, s3
-; GFX90A-NEXT: s_add_i32 s3, s6, s1
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s6, s1, s9
-; GFX90A-NEXT: s_xor_b32 s1, s3, s1
-; GFX90A-NEXT: s_sub_i32 s3, 0, s2
-; GFX90A-NEXT: v_mul_f32_e32 v0, s10, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v1, s3, v0
-; GFX90A-NEXT: v_mul_hi_u32 v1, v0, v1
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v1
-; GFX90A-NEXT: v_mul_hi_u32 v0, s1, v0
-; GFX90A-NEXT: v_mul_lo_u32 v1, v0, s2
-; GFX90A-NEXT: v_sub_u32_e32 v1, s1, v1
-; GFX90A-NEXT: s_ashr_i32 s1, s0, 31
-; GFX90A-NEXT: s_add_i32 s0, s0, s1
-; GFX90A-NEXT: s_xor_b32 s0, s0, s1
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v4, s0
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v3, s2, v1
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s2, v1
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v1, v4
-; GFX90A-NEXT: s_ashr_i32 s2, s7, 31
-; GFX90A-NEXT: s_add_i32 s3, s7, s2
-; GFX90A-NEXT: v_add_u32_e32 v3, 1, v0
-; GFX90A-NEXT: v_mul_f32_e32 v1, s10, v1
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v1, v1
-; GFX90A-NEXT: s_xor_b32 s1, s2, s1
-; GFX90A-NEXT: s_xor_b32 s2, s3, s2
-; GFX90A-NEXT: s_sub_i32 s3, 0, s0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
-; GFX90A-NEXT: v_mul_lo_u32 v3, s3, v1
-; GFX90A-NEXT: v_mul_hi_u32 v3, v1, v3
-; GFX90A-NEXT: v_add_u32_e32 v1, v1, v3
-; GFX90A-NEXT: v_mul_hi_u32 v1, s2, v1
-; GFX90A-NEXT: v_mul_lo_u32 v3, v1, s0
-; GFX90A-NEXT: v_sub_u32_e32 v3, s2, v3
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v4, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v3, v3, v4, vcc
-; GFX90A-NEXT: v_add_u32_e32 v4, 1, v1
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s0, v3
-; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v0, s6, v0
-; GFX90A-NEXT: v_xor_b32_e32 v1, s1, v1
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s6, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v1, s1, v1
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
-; GFX90A-NEXT: s_endpgm
%shl.y = shl <2 x i32> <i32 4096, i32 4096>, %y
%r = sdiv <2 x i32> %x, %shl.y
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i32_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i32 %x, 1235195
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dword v0, v1, s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i32 %x, 4096
store i32 %r, i32 addrspace(1)* %out
ret void
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_dword v1, v0, s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: srem_i32_pow2_shl_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0x2c
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshl_b32 s3, 0x1000, s3
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_add_i32 s3, s3, s4
-; GFX90A-NEXT: s_xor_b32 s3, s3, s4
-; GFX90A-NEXT: v_cvt_f32_u32_e32 v0, s3
-; GFX90A-NEXT: s_sub_i32 s5, 0, s3
-; GFX90A-NEXT: s_ashr_i32 s4, s2, 31
-; GFX90A-NEXT: s_add_i32 s2, s2, s4
-; GFX90A-NEXT: v_rcp_iflag_f32_e32 v0, v0
-; GFX90A-NEXT: s_xor_b32 s2, s2, s4
-; GFX90A-NEXT: v_mul_f32_e32 v0, 0x4f7ffffe, v0
-; GFX90A-NEXT: v_cvt_u32_f32_e32 v0, v0
-; GFX90A-NEXT: v_mul_lo_u32 v2, s5, v0
-; GFX90A-NEXT: v_mul_hi_u32 v2, v0, v2
-; GFX90A-NEXT: v_add_u32_e32 v0, v0, v2
-; GFX90A-NEXT: v_mul_hi_u32 v0, s2, v0
-; GFX90A-NEXT: v_mul_lo_u32 v0, v0, s3
-; GFX90A-NEXT: v_sub_u32_e32 v0, s2, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_subrev_u32_e32 v2, s3, v0
-; GFX90A-NEXT: v_cmp_le_u32_e32 vcc, s3, v0
-; GFX90A-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
-; GFX90A-NEXT: v_xor_b32_e32 v0, s4, v0
-; GFX90A-NEXT: v_subrev_u32_e32 v0, s4, v0
-; GFX90A-NEXT: global_store_dword v1, v0, s[0:1]
-; GFX90A-NEXT: s_endpgm
%shl.y = shl i32 4096, %y
%r = srem i32 %x, %shl.y
store i32 %r, i32 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v1, s0
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v2i32_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <2 x i32> %x, <i32 4096, i32 4096>
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
ret void
; GFX9-NEXT: v_subrev_u32_e32 v1, s6, v1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v2i32_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subrev_u32_e32 v1, s1, v1
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl <2 x i32> <i32 4096, i32 4096>, %y
%r = srem <2 x i32> %x, %shl.y
store <2 x i32> %r, <2 x i32> addrspace(1)* %out
; GFX9-NEXT: v_cmp_lt_u32_e64 s[0:1], s6, v5
; GFX9-NEXT: v_cndmask_b32_e64 v5, 0, -1, s[0:1]
; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], s2, v4
+<<<<<<< HEAD
; GFX9-NEXT: v_cndmask_b32_e64 v4, v7, v5, s[0:1]
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v7, v6, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v6, s[0:1], 2, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v7, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v8, s[0:1], 1, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v9, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v9, v7, s[0:1]
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_mov_b32_e32 v7, s7
; GFX9-NEXT: v_subb_co_u32_e32 v2, vcc, v7, v2, vcc
; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s3, v2
-; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v4
; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s6, v3
-; GFX9-NEXT: v_cndmask_b32_e64 v4, 1, 2, s[0:1]
; GFX9-NEXT: v_cndmask_b32_e64 v3, 0, -1, vcc
; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s2, v2
-; GFX9-NEXT: v_add_co_u32_e64 v4, s[0:1], v0, v4
; GFX9-NEXT: v_cndmask_b32_e32 v2, v7, v3, vcc
; GFX9-NEXT: v_addc_co_u32_e64 v5, s[0:1], 0, v1, s[0:1]
; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v2
+<<<<<<< HEAD
; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v4, vcc
; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v5, vcc
; GFX9-NEXT: global_store_dwordx2 v6, v[0:1], s[4:5]
; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v6, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v8, v6, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
+; GFX9-NEXT: global_store_dwordx2 v5, v[0:1], s[4:5]
+; GFX9-NEXT: s_endpgm
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv i64 %x, 1235195949943
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s3
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: udiv_i64_pow2k_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_lshr_b64 s[2:3], s[2:3], 12
-; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[2:3], s[2:3] op_sel:[0,1]
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = udiv i64 %x, 4096
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[0:1], s[0:1] op_sel:[0,1]
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i64 4096, %y
%r = udiv i64 %x, %shl.y
store i64 %r, i64 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v3, s5
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v2i64_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v3, s5
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <2 x i64> %x, <i64 4096, i64 4096>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mac_f32_e32 v0, 0xcf800000, v1
; GFX9-NEXT: v_cvt_u32_f32_e32 v0, v0
; GFX9-NEXT: v_cvt_u32_f32_e32 v1, v1
+<<<<<<< HEAD
; GFX9-NEXT: s_movk_i32 s8, 0xfff
; GFX9-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
; GFX9-NEXT: v_mul_hi_u32 v2, v0, s2
; GFX9-NEXT: v_mul_lo_u32 v4, v1, s2
; GFX9-NEXT: v_mul_lo_u32 v3, v0, s2
+=======
+; GFX9-NEXT: v_mul_hi_u32 v2, v0, s4
+; GFX9-NEXT: v_mul_lo_u32 v4, v1, s4
+; GFX9-NEXT: v_mul_lo_u32 v3, v0, s4
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_sub_u32_e32 v2, v2, v0
; GFX9-NEXT: v_add_u32_e32 v2, v2, v4
; GFX9-NEXT: v_mul_hi_u32 v6, v0, v3
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v6, v3, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v8, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v4, vcc
+=======
+; GFX9-NEXT: v_add_co_u32_e64 v0, s[2:3], v0, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v2, vcc, v1, v3, s[2:3]
+; GFX9-NEXT: v_mul_hi_u32 v4, v0, s4
+; GFX9-NEXT: v_mul_lo_u32 v6, v2, s4
+; GFX9-NEXT: v_mul_lo_u32 v8, v0, s4
+; GFX9-NEXT: s_load_dwordx2 s[8:9], s[0:1], 0x24
+; GFX9-NEXT: s_load_dwordx4 s[4:7], s[0:1], 0x34
+; GFX9-NEXT: v_sub_u32_e32 v4, v4, v0
+; GFX9-NEXT: v_add_u32_e32 v4, v4, v6
+; GFX9-NEXT: v_mul_lo_u32 v6, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v9, v0, v8
+; GFX9-NEXT: v_mul_hi_u32 v10, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v11, v2, v4
+; GFX9-NEXT: v_add_u32_e32 v1, v1, v3
+; GFX9-NEXT: v_add_co_u32_e32 v6, vcc, v9, v6
+; GFX9-NEXT: v_addc_co_u32_e32 v9, vcc, v7, v10, vcc
+; GFX9-NEXT: v_mul_lo_u32 v10, v2, v8
+; GFX9-NEXT: v_mul_hi_u32 v8, v2, v8
+; GFX9-NEXT: v_mul_lo_u32 v2, v2, v4
+; GFX9-NEXT: s_movk_i32 s0, 0xfff
+; GFX9-NEXT: v_add_co_u32_e32 v6, vcc, v6, v10
+; GFX9-NEXT: v_addc_co_u32_e32 v6, vcc, v9, v8, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v11, v5, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v6, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v1, vcc, v1, v4, s[2:3]
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: v_mul_hi_u32 v2, v0, s2
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v4, vcc
; GFX9-NEXT: v_mul_lo_u32 v4, s7, v0
; GFX9-NEXT: v_mul_hi_u32 v0, s7, v0
+<<<<<<< HEAD
+=======
+; GFX9-NEXT: s_lshr_b64 s[2:3], s[4:5], 12
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v4
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, v3, v0, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v6, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v2, vcc
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s8
; GFX9-NEXT: v_mul_hi_u32 v3, v0, s8
; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v2
; GFX9-NEXT: v_cndmask_b32_e32 v2, v0, v3, vcc
; GFX9-NEXT: v_cndmask_b32_e32 v3, v1, v6, vcc
+=======
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v7, v2, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, 2, v0
+; GFX9-NEXT: v_mul_lo_u32 v4, v1, s0
+; GFX9-NEXT: v_mul_hi_u32 v6, v0, s0
+; GFX9-NEXT: v_mul_lo_u32 v9, v0, s0
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v7, vcc, 1, v0
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_u32_e32 v4, v6, v4
+; GFX9-NEXT: v_mov_b32_e32 v6, s7
+; GFX9-NEXT: v_sub_co_u32_e32 v9, vcc, s6, v9
+; GFX9-NEXT: v_subb_co_u32_e32 v4, vcc, v6, v4, vcc
+; GFX9-NEXT: v_subrev_co_u32_e32 v6, vcc, s0, v9
+; GFX9-NEXT: v_subbrev_co_u32_e32 v10, vcc, 0, v4, vcc
+; GFX9-NEXT: s_movk_i32 s0, 0xffe
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, 0, v10
+; GFX9-NEXT: v_cndmask_b32_e32 v6, -1, v6, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e64 s[0:1], s0, v9
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, -1, v6, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v3, v8, v3, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v3, v1, v3, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v7, v2, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v0, v1, s[0:1]
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_mov_b32_e32 v0, s2
; GFX9-NEXT: v_mov_b32_e32 v1, s3
-; GFX9-NEXT: global_store_dwordx4 v5, v[0:3], s[0:1]
+; GFX9-NEXT: global_store_dwordx4 v5, v[0:3], s[8:9]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v2i64_mixed_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = udiv <2 x i64> %x, <i64 4096, i64 4095>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v3, s5
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: udiv_v2i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v3, s5
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl <2 x i64> <i64 4096, i64 4096>, %y
%r = udiv <2 x i64> %x, %shl.y
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
; GFX9-NEXT: v_cndmask_b32_e64 v7, v7, v8, s[2:3]
; GFX9-NEXT: v_subbrev_co_u32_e64 v2, s[0:1], 0, v2, s[0:1]
; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v7
+<<<<<<< HEAD
; GFX9-NEXT: v_cndmask_b32_e64 v3, v4, v3, s[0:1]
; GFX9-NEXT: v_mov_b32_e32 v4, s7
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v4, v1, vcc
; GFX90A-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v6, v2, s[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v6, s7
+; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v6, v1, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s6, v1
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s10, v0
+; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s8, v1
+; GFX9-NEXT: v_cndmask_b32_e32 v6, v6, v7, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v6
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v2, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v4, v3, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
+; GFX9-NEXT: global_store_dwordx2 v5, v[0:1], s[4:5]
+; GFX9-NEXT: s_endpgm
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem i64 %x, 1235195393993
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v0, s2
; GFX9-NEXT: global_store_dwordx2 v1, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: urem_i64_pow2k_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v1, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_and_b32 s2, s2, 0xfff
-; GFX90A-NEXT: v_mov_b32_e32 v0, s2
-; GFX90A-NEXT: global_store_dwordx2 v1, v[0:1], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = urem i64 %x, 4096
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s1
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[0:1], s[0:1] op_sel:[0,1]
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i64 4096, %y
%r = urem i64 %x, %shl.y
store i64 %r, i64 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v2, s0
; GFX9-NEXT: global_store_dwordx4 v1, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v2i64_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v2, s0
; GFX90A-NEXT: global_store_dwordx4 v1, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = urem <2 x i64> %x, <i64 4096, i64 4096>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v3, s5
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: urem_v2i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v3, s5
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl <2 x i64> <i64 4096, i64 4096>, %y
%r = urem <2 x i64> %x, %shl.y
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
; GFX9-NEXT: v_trunc_f32_e32 v1, v1
; GFX9-NEXT: v_mac_f32_e32 v0, 0xcf800000, v1
; GFX9-NEXT: v_cvt_u32_f32_e32 v1, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_cvt_u32_f32_e32 v0, v0
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s4
; GFX9-NEXT: v_mul_hi_u32 v3, v0, s4
; GFX9-NEXT: v_mul_lo_u32 v4, v0, s4
+=======
+; GFX9-NEXT: s_load_dwordx4 s[4:7], s[0:1], 0x24
+; GFX9-NEXT: v_mul_hi_u32 v3, v0, s8
+; GFX9-NEXT: v_mul_lo_u32 v2, v1, s8
+; GFX9-NEXT: v_mul_lo_u32 v4, v0, s8
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_u32_e32 v2, v3, v2
; GFX9-NEXT: v_sub_u32_e32 v2, v2, v0
; GFX9-NEXT: v_mul_hi_u32 v3, v0, v4
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v6, v4, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v8, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v4, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: v_mul_lo_u32 v4, s3, v0
; GFX9-NEXT: v_mul_hi_u32 v0, s3, v0
; GFX9-NEXT: s_mov_b32 s5, 0x12d8fb
+=======
+; GFX9-NEXT: v_add_co_u32_e64 v0, s[2:3], v0, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v2, vcc, v1, v3, s[2:3]
+; GFX9-NEXT: v_mul_lo_u32 v4, v2, s8
+; GFX9-NEXT: v_mul_hi_u32 v6, v0, s8
+; GFX9-NEXT: v_mul_lo_u32 v8, v0, s8
+; GFX9-NEXT: v_add_u32_e32 v1, v1, v3
+; GFX9-NEXT: v_add_u32_e32 v4, v6, v4
+; GFX9-NEXT: v_sub_u32_e32 v4, v4, v0
+; GFX9-NEXT: v_mul_lo_u32 v10, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v11, v0, v8
+; GFX9-NEXT: v_mul_hi_u32 v12, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v9, v2, v8
+; GFX9-NEXT: v_mul_lo_u32 v8, v2, v8
+; GFX9-NEXT: v_add_co_u32_e32 v10, vcc, v11, v10
+; GFX9-NEXT: v_mul_hi_u32 v6, v2, v4
+; GFX9-NEXT: v_addc_co_u32_e32 v11, vcc, v7, v12, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, v2, v4
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v10, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v11, v9, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v6, v5, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v8, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v1, vcc, v1, v4, s[2:3]
+; GFX9-NEXT: s_waitcnt lgkmcnt(0)
+; GFX9-NEXT: s_ashr_i32 s2, s7, 31
+; GFX9-NEXT: s_add_u32 s0, s6, s2
+; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
+; GFX9-NEXT: s_mov_b32 s3, s2
+; GFX9-NEXT: s_addc_u32 s1, s7, s2
+; GFX9-NEXT: s_xor_b64 s[0:1], s[0:1], s[2:3]
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v1, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, s0, v1
+; GFX9-NEXT: v_mul_hi_u32 v3, s0, v0
+; GFX9-NEXT: v_mul_hi_u32 v4, s0, v1
+; GFX9-NEXT: v_mul_hi_u32 v6, s1, v1
+; GFX9-NEXT: v_mul_lo_u32 v1, s1, v1
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v7, v4, vcc
+; GFX9-NEXT: v_mul_lo_u32 v4, s1, v0
+; GFX9-NEXT: v_mul_hi_u32 v0, s1, v0
+; GFX9-NEXT: s_mov_b32 s3, 0x12d8fb
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v4
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, v3, v0, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v6, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v2, vcc
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s5
; GFX9-NEXT: v_mul_hi_u32 v3, v0, s5
; GFX9-NEXT: v_xor_b32_e32 v1, s4, v1
; GFX9-NEXT: v_mov_b32_e32 v2, s4
; GFX9-NEXT: v_subrev_co_u32_e32 v0, vcc, s4, v0
+=======
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v7, v2, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, 2, v0
+; GFX9-NEXT: v_mul_lo_u32 v4, v1, s3
+; GFX9-NEXT: v_mul_hi_u32 v6, v0, s3
+; GFX9-NEXT: v_mul_lo_u32 v9, v0, s3
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v7, vcc, 1, v0
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_u32_e32 v4, v6, v4
+; GFX9-NEXT: v_sub_co_u32_e32 v9, vcc, s0, v9
+; GFX9-NEXT: v_mov_b32_e32 v6, s1
+; GFX9-NEXT: v_subb_co_u32_e32 v4, vcc, v6, v4, vcc
+; GFX9-NEXT: v_subrev_co_u32_e32 v6, vcc, s3, v9
+; GFX9-NEXT: v_subbrev_co_u32_e32 v10, vcc, 0, v4, vcc
+; GFX9-NEXT: s_mov_b32 s0, 0x12d8fa
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, 0, v10
+; GFX9-NEXT: v_cndmask_b32_e32 v6, -1, v6, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e64 s[0:1], s0, v9
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, -1, v6, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v7, v2, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v0, v0, v2, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v3, v8, v3, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v1, v1, v3, s[0:1]
+; GFX9-NEXT: v_xor_b32_e32 v0, s2, v0
+; GFX9-NEXT: v_xor_b32_e32 v1, s2, v1
+; GFX9-NEXT: v_mov_b32_e32 v2, s2
+; GFX9-NEXT: v_subrev_co_u32_e32 v0, vcc, s2, v0
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v2, vcc
; GFX9-NEXT: global_store_dwordx2 v5, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i64_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv i64 %x, 1235195
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s3
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: sdiv_i64_pow2k_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_lshr_b32 s4, s4, 20
-; GFX90A-NEXT: s_add_u32 s2, s2, s4
-; GFX90A-NEXT: s_addc_u32 s3, s3, 0
-; GFX90A-NEXT: s_ashr_i64 s[2:3], s[2:3], 12
-; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[2:3], s[2:3] op_sel:[0,1]
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = sdiv i64 %x, 4096
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], s9, v5
; GFX9-NEXT: v_cndmask_b32_e64 v5, v7, v6, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v6, s[0:1], 2, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v7, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v8, s[0:1], 1, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v9, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v5
+; GFX9-NEXT: v_cndmask_b32_e64 v5, v9, v7, s[0:1]
; GFX9-NEXT: v_mov_b32_e32 v7, s7
; GFX9-NEXT: v_subb_co_u32_e32 v3, vcc, v7, v3, vcc
+<<<<<<< HEAD
; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s9, v3
; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v5
; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
; GFX9-NEXT: v_cndmask_b32_e64 v4, 0, -1, vcc
; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s9, v3
; GFX9-NEXT: v_add_co_u32_e64 v5, s[0:1], v0, v5
+=======
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s11, v3
+; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s10, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s11, v3
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_cndmask_b32_e32 v3, v7, v4, vcc
-; GFX9-NEXT: v_addc_co_u32_e64 v6, s[0:1], 0, v1, s[0:1]
; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v3
+<<<<<<< HEAD
; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v5, vcc
; GFX9-NEXT: s_xor_b64 s[0:1], s[10:11], s[2:3]
; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v6, vcc
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v3, v8, v6, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
+; GFX9-NEXT: s_xor_b64 s[0:1], s[2:3], s[8:9]
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v5, vcc
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_xor_b32_e32 v0, s0, v0
; GFX9-NEXT: v_xor_b32_e32 v1, s1, v1
; GFX9-NEXT: v_mov_b32_e32 v3, s1
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i64 4096, %y
%r = sdiv i64 %x, %shl.y
store i64 %r, i64 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v3, s5
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v2i64_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v3, s5
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <2 x i64> %x, <i64 4096, i64 4096>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_addc_co_u32_e32 v6, vcc, 0, v7, vcc
; GFX9-NEXT: v_mul_lo_u32 v7, v1, v5
; GFX9-NEXT: v_mul_hi_u32 v5, v1, v5
+<<<<<<< HEAD
; GFX9-NEXT: s_ashr_i64 s[2:3], s[2:3], 12
; GFX9-NEXT: s_ashr_i32 s4, s7, 31
+=======
+; GFX9-NEXT: s_ashr_i64 s[4:5], s[4:5], 12
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v3, vcc, v3, v7
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v6, v5, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v5, vcc, v8, v4, vcc
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v5, vcc
+=======
+; GFX9-NEXT: v_mov_b32_e32 v6, 0
+; GFX9-NEXT: v_add_co_u32_e64 v0, s[2:3], v0, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v6, v5, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v2, vcc, v1, v3, s[2:3]
+; GFX9-NEXT: v_mul_lo_u32 v5, v2, s8
+; GFX9-NEXT: v_mul_hi_u32 v7, v0, s8
+; GFX9-NEXT: v_mul_lo_u32 v8, v0, s8
+; GFX9-NEXT: v_add_u32_e32 v1, v1, v3
+; GFX9-NEXT: s_load_dwordx2 s[8:9], s[0:1], 0x24
+; GFX9-NEXT: v_add_u32_e32 v5, v7, v5
+; GFX9-NEXT: v_sub_u32_e32 v5, v5, v0
+; GFX9-NEXT: v_mul_lo_u32 v10, v0, v5
+; GFX9-NEXT: v_mul_hi_u32 v11, v0, v8
+; GFX9-NEXT: v_mul_hi_u32 v12, v0, v5
+; GFX9-NEXT: v_mul_hi_u32 v9, v2, v8
+; GFX9-NEXT: v_mul_lo_u32 v8, v2, v8
+; GFX9-NEXT: v_add_co_u32_e32 v10, vcc, v11, v10
+; GFX9-NEXT: v_mul_hi_u32 v7, v2, v5
+; GFX9-NEXT: v_addc_co_u32_e32 v11, vcc, 0, v12, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, v2, v5
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v10, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v11, v9, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v5, vcc, v7, v4, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v8, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v5, vcc, v6, v5, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v1, vcc, v1, v5, s[2:3]
+; GFX9-NEXT: s_ashr_i32 s2, s7, 31
+; GFX9-NEXT: s_add_u32 s6, s6, s2
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s8
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v5, vcc
; GFX9-NEXT: v_mul_lo_u32 v5, s7, v0
; GFX9-NEXT: v_mul_hi_u32 v0, s7, v0
+<<<<<<< HEAD
; GFX9-NEXT: s_movk_i32 s5, 0xfff
; GFX9-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x24
+=======
+; GFX9-NEXT: s_movk_i32 s0, 0xfff
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v5
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, v3, v0, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v6, v4, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v2, vcc
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s5
; GFX9-NEXT: v_mul_hi_u32 v3, v0, s5
; GFX9-NEXT: v_xor_b32_e32 v1, s4, v1
; GFX9-NEXT: v_mov_b32_e32 v3, s4
; GFX9-NEXT: v_subrev_co_u32_e32 v2, vcc, s4, v0
+=======
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v6, v2, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, 2, v0
+; GFX9-NEXT: v_mul_lo_u32 v5, v1, s0
+; GFX9-NEXT: v_mul_hi_u32 v6, v0, s0
+; GFX9-NEXT: v_mul_lo_u32 v9, v0, s0
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v7, vcc, 1, v0
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, 0, v1, vcc
+; GFX9-NEXT: v_add_u32_e32 v5, v6, v5
+; GFX9-NEXT: v_mov_b32_e32 v6, s7
+; GFX9-NEXT: v_sub_co_u32_e32 v9, vcc, s6, v9
+; GFX9-NEXT: v_subb_co_u32_e32 v5, vcc, v6, v5, vcc
+; GFX9-NEXT: v_subrev_co_u32_e32 v6, vcc, s0, v9
+; GFX9-NEXT: v_subbrev_co_u32_e32 v10, vcc, 0, v5, vcc
+; GFX9-NEXT: s_movk_i32 s0, 0xffe
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, 0, v10
+; GFX9-NEXT: v_cndmask_b32_e32 v6, -1, v6, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e64 s[0:1], s0, v9
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v6
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], 0, v5
+; GFX9-NEXT: v_cndmask_b32_e64 v5, -1, v6, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v5
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v7, v2, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v0, v0, v2, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v3, v8, v3, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v1, v1, v3, s[0:1]
+; GFX9-NEXT: v_xor_b32_e32 v0, s2, v0
+; GFX9-NEXT: v_subrev_co_u32_e32 v2, vcc, s2, v0
+; GFX9-NEXT: v_xor_b32_e32 v1, s2, v1
+; GFX9-NEXT: v_mov_b32_e32 v3, s2
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_subb_co_u32_e32 v3, vcc, v1, v3, vcc
; GFX9-NEXT: v_mov_b32_e32 v0, s2
; GFX9-NEXT: v_mov_b32_e32 v1, s3
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
-; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[0:1]
+; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[8:9]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: ssdiv_v2i64_mixed_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v1, s1
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = sdiv <2 x i64> %x, <i64 4096, i64 4095>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_subbrev_co_u32_e64 v5, s[0:1], 0, v5, s[0:1]
; GFX9-NEXT: v_cmp_le_u32_e64 s[0:1], s11, v5
; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, s[0:1]
+<<<<<<< HEAD
; GFX9-NEXT: v_cmp_le_u32_e64 s[0:1], s10, v6
; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], s11, v5
; GFX9-NEXT: v_subb_co_u32_e32 v2, vcc, v2, v7, vcc
; GFX9-NEXT: v_add_u32_e32 v5, v5, v8
; GFX9-NEXT: v_sub_u32_e32 v7, s7, v5
+=======
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], s11, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v8, v7, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v7, s[0:1], 2, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v8, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v9, s[0:1], 1, v0
+; GFX9-NEXT: v_addc_co_u32_e64 v10, s[0:1], 0, v1, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v10, v8, s[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v8, s5
+; GFX9-NEXT: s_xor_b64 s[4:5], s[14:15], s[12:13]
+; GFX9-NEXT: s_ashr_i32 s12, s9, 31
+; GFX9-NEXT: s_add_u32 s8, s8, s12
+; GFX9-NEXT: s_mov_b32 s13, s12
+; GFX9-NEXT: s_addc_u32 s9, s9, s12
+; GFX9-NEXT: s_xor_b64 s[8:9], s[8:9], s[12:13]
+; GFX9-NEXT: v_cvt_f32_u32_e32 v10, s8
+; GFX9-NEXT: v_cvt_f32_u32_e32 v11, s9
+; GFX9-NEXT: v_subb_co_u32_e32 v2, vcc, v8, v2, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s11, v2
+; GFX9-NEXT: v_cndmask_b32_e64 v8, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s10, v3
+; GFX9-NEXT: v_cndmask_b32_e64 v3, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s11, v2
+; GFX9-NEXT: v_mac_f32_e32 v10, s16, v11
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v8, v3, vcc
+; GFX9-NEXT: v_rcp_f32_e32 v3, v10
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v2
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v4, vcc
+; GFX9-NEXT: s_sub_u32 s10, 0, s8
+; GFX9-NEXT: v_mul_f32_e32 v3, s17, v3
+; GFX9-NEXT: v_mul_f32_e32 v4, s18, v3
+; GFX9-NEXT: v_trunc_f32_e32 v4, v4
+; GFX9-NEXT: v_mac_f32_e32 v3, s19, v4
+; GFX9-NEXT: v_cvt_u32_f32_e32 v4, v4
+; GFX9-NEXT: v_cvt_u32_f32_e32 v3, v3
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v9, v7, s[0:1]
+; GFX9-NEXT: s_subb_u32 s11, 0, s9
+; GFX9-NEXT: v_mul_lo_u32 v8, s10, v4
+; GFX9-NEXT: v_mul_hi_u32 v7, s10, v3
+; GFX9-NEXT: v_mul_lo_u32 v9, s11, v3
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, s10, v3
+; GFX9-NEXT: v_add_u32_e32 v7, v7, v8
+; GFX9-NEXT: v_add_u32_e32 v7, v7, v9
+; GFX9-NEXT: v_mul_lo_u32 v8, v3, v7
+; GFX9-NEXT: v_mul_hi_u32 v9, v3, v2
+; GFX9-NEXT: v_mul_hi_u32 v10, v3, v7
+; GFX9-NEXT: v_mul_hi_u32 v11, v4, v7
+; GFX9-NEXT: v_mul_lo_u32 v7, v4, v7
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v9, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v9, vcc, 0, v10, vcc
+; GFX9-NEXT: v_mul_lo_u32 v10, v4, v2
+; GFX9-NEXT: v_mul_hi_u32 v2, v4, v2
+; GFX9-NEXT: v_xor_b32_e32 v0, s4, v0
+; GFX9-NEXT: v_xor_b32_e32 v1, s5, v1
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v8, v10
+; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v9, v2, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v11, v6, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v7
+; GFX9-NEXT: v_add_co_u32_e64 v2, s[0:1], v3, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v7, vcc, v5, v8, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v3, vcc, v4, v7, s[0:1]
+; GFX9-NEXT: v_mul_lo_u32 v8, s10, v3
+; GFX9-NEXT: v_mul_hi_u32 v9, s10, v2
+; GFX9-NEXT: v_mul_lo_u32 v10, s11, v2
+; GFX9-NEXT: v_mul_lo_u32 v11, s10, v2
+; GFX9-NEXT: s_ashr_i32 s10, s7, 31
+; GFX9-NEXT: v_add_u32_e32 v8, v9, v8
+; GFX9-NEXT: v_add_u32_e32 v8, v8, v10
+; GFX9-NEXT: v_mul_lo_u32 v12, v2, v8
+; GFX9-NEXT: v_mul_hi_u32 v13, v2, v11
+; GFX9-NEXT: v_mul_hi_u32 v14, v2, v8
+; GFX9-NEXT: v_mul_hi_u32 v10, v3, v11
+; GFX9-NEXT: v_mul_lo_u32 v11, v3, v11
+; GFX9-NEXT: v_add_co_u32_e32 v12, vcc, v13, v12
+; GFX9-NEXT: v_mul_hi_u32 v9, v3, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v13, vcc, 0, v14, vcc
+; GFX9-NEXT: v_mul_lo_u32 v3, v3, v8
+; GFX9-NEXT: v_add_co_u32_e32 v11, vcc, v12, v11
+; GFX9-NEXT: v_addc_co_u32_e32 v10, vcc, v13, v10, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v9, v6, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v3, vcc, v10, v3
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v5, v8, vcc
+; GFX9-NEXT: v_add_u32_e32 v4, v4, v7
+; GFX9-NEXT: v_addc_co_u32_e64 v4, vcc, v4, v8, s[0:1]
+; GFX9-NEXT: s_add_u32 s0, s6, s10
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v3
+; GFX9-NEXT: s_mov_b32 s11, s10
+; GFX9-NEXT: s_addc_u32 s1, s7, s10
+; GFX9-NEXT: s_xor_b64 s[6:7], s[0:1], s[10:11]
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v4, vcc
+; GFX9-NEXT: v_mul_lo_u32 v4, s6, v3
+; GFX9-NEXT: v_mul_hi_u32 v7, s6, v2
+; GFX9-NEXT: v_mul_hi_u32 v9, s6, v3
+; GFX9-NEXT: v_mul_hi_u32 v10, s7, v3
+; GFX9-NEXT: v_mul_lo_u32 v3, s7, v3
+; GFX9-NEXT: v_add_co_u32_e32 v4, vcc, v7, v4
+; GFX9-NEXT: v_addc_co_u32_e32 v7, vcc, 0, v9, vcc
+; GFX9-NEXT: v_mul_lo_u32 v9, s7, v2
+; GFX9-NEXT: v_mul_hi_u32 v2, s7, v2
+; GFX9-NEXT: v_mov_b32_e32 v8, s5
+; GFX9-NEXT: v_add_co_u32_e32 v4, vcc, v4, v9
+; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v7, v2, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v10, v6, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v3
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v5, v4, vcc
+; GFX9-NEXT: v_mul_lo_u32 v4, s8, v3
+; GFX9-NEXT: v_mul_hi_u32 v5, s8, v2
+; GFX9-NEXT: v_mul_lo_u32 v7, s9, v2
+; GFX9-NEXT: v_subrev_co_u32_e32 v0, vcc, s4, v0
+; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v8, vcc
+; GFX9-NEXT: v_add_u32_e32 v4, v5, v4
+; GFX9-NEXT: v_mul_lo_u32 v5, s8, v2
+; GFX9-NEXT: v_add_u32_e32 v4, v4, v7
+; GFX9-NEXT: v_sub_u32_e32 v7, s7, v4
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_mov_b32_e32 v8, s9
; GFX9-NEXT: v_sub_co_u32_e32 v6, vcc, s6, v6
; GFX9-NEXT: v_subb_co_u32_e64 v7, s[0:1], v7, v8, vcc
; GFX9-NEXT: v_cndmask_b32_e64 v8, 0, -1, s[0:1]
; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], s9, v7
; GFX9-NEXT: v_cndmask_b32_e64 v7, v9, v8, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v8, s[0:1], 2, v2
+; GFX9-NEXT: v_addc_co_u32_e64 v9, s[0:1], 0, v3, s[0:1]
+; GFX9-NEXT: v_add_co_u32_e64 v10, s[0:1], 1, v2
+; GFX9-NEXT: v_addc_co_u32_e64 v11, s[0:1], 0, v3, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v7
+; GFX9-NEXT: v_cndmask_b32_e64 v7, v11, v9, s[0:1]
; GFX9-NEXT: v_mov_b32_e32 v9, s7
+<<<<<<< HEAD
; GFX9-NEXT: v_subb_co_u32_e32 v5, vcc, v9, v5, vcc
; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s9, v5
; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v7
; GFX9-NEXT: v_mov_b32_e32 v5, s1
; GFX9-NEXT: v_subrev_co_u32_e32 v3, vcc, s0, v3
; GFX9-NEXT: v_subb_co_u32_e32 v4, vcc, v4, v5, vcc
+=======
+; GFX9-NEXT: v_subb_co_u32_e32 v4, vcc, v9, v4, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s9, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v9, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s8, v5
+; GFX9-NEXT: v_cndmask_b32_e64 v5, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s9, v4
+; GFX9-NEXT: v_cndmask_b32_e32 v4, v9, v5, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v10, v8, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v2, v4, vcc
+; GFX9-NEXT: s_xor_b64 s[0:1], s[10:11], s[12:13]
+; GFX9-NEXT: v_cndmask_b32_e32 v3, v3, v7, vcc
+; GFX9-NEXT: v_xor_b32_e32 v2, s0, v2
+; GFX9-NEXT: v_xor_b32_e32 v3, s1, v3
+; GFX9-NEXT: v_mov_b32_e32 v4, s1
+; GFX9-NEXT: v_subrev_co_u32_e32 v2, vcc, s0, v2
+; GFX9-NEXT: v_subb_co_u32_e32 v3, vcc, v3, v4, vcc
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_dwordx4 v0, v[1:4], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: sdiv_v2i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v3, vcc, v3, v5, vcc
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl <2 x i64> <i64 4096, i64 4096>, %y
%r = sdiv <2 x i64> %x, %shl.y
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
; GFX9-NEXT: v_trunc_f32_e32 v1, v1
; GFX9-NEXT: v_mac_f32_e32 v0, 0xcf800000, v1
; GFX9-NEXT: v_cvt_u32_f32_e32 v1, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_cvt_u32_f32_e32 v0, v0
; GFX9-NEXT: v_mul_lo_u32 v2, v1, s4
; GFX9-NEXT: v_mul_hi_u32 v3, v0, s4
; GFX9-NEXT: v_mul_lo_u32 v4, v0, s4
+=======
+; GFX9-NEXT: s_load_dwordx4 s[4:7], s[0:1], 0x24
+; GFX9-NEXT: v_mul_hi_u32 v3, v0, s8
+; GFX9-NEXT: v_mul_lo_u32 v2, v1, s8
+; GFX9-NEXT: v_mul_lo_u32 v4, v0, s8
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_u32_e32 v2, v3, v2
; GFX9-NEXT: v_sub_u32_e32 v2, v2, v0
; GFX9-NEXT: v_mul_hi_u32 v3, v0, v4
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v6, v4, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v8, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, 0, v4, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: v_mul_lo_u32 v4, s3, v0
; GFX9-NEXT: v_mul_hi_u32 v0, s3, v0
; GFX9-NEXT: s_mov_b32 s5, 0x12d8fb
+=======
+; GFX9-NEXT: v_add_co_u32_e64 v0, s[2:3], v0, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v2, vcc, v1, v3, s[2:3]
+; GFX9-NEXT: v_mul_lo_u32 v4, v2, s8
+; GFX9-NEXT: v_mul_hi_u32 v6, v0, s8
+; GFX9-NEXT: v_mul_lo_u32 v8, v0, s8
+; GFX9-NEXT: v_add_u32_e32 v1, v1, v3
+; GFX9-NEXT: v_add_u32_e32 v4, v6, v4
+; GFX9-NEXT: v_sub_u32_e32 v4, v4, v0
+; GFX9-NEXT: v_mul_lo_u32 v10, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v11, v0, v8
+; GFX9-NEXT: v_mul_hi_u32 v12, v0, v4
+; GFX9-NEXT: v_mul_hi_u32 v9, v2, v8
+; GFX9-NEXT: v_mul_lo_u32 v8, v2, v8
+; GFX9-NEXT: v_add_co_u32_e32 v10, vcc, v11, v10
+; GFX9-NEXT: v_mul_hi_u32 v6, v2, v4
+; GFX9-NEXT: v_addc_co_u32_e32 v11, vcc, v7, v12, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, v2, v4
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v10, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, v11, v9, vcc
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v6, v5, vcc
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v8, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v4, vcc, v7, v4, vcc
+; GFX9-NEXT: v_addc_co_u32_e64 v1, vcc, v1, v4, s[2:3]
+; GFX9-NEXT: s_waitcnt lgkmcnt(0)
+; GFX9-NEXT: s_ashr_i32 s2, s7, 31
+; GFX9-NEXT: s_add_u32 s0, s6, s2
+; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v2
+; GFX9-NEXT: s_mov_b32 s3, s2
+; GFX9-NEXT: s_addc_u32 s1, s7, s2
+; GFX9-NEXT: s_xor_b64 s[0:1], s[0:1], s[2:3]
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v1, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, s0, v1
+; GFX9-NEXT: v_mul_hi_u32 v3, s0, v0
+; GFX9-NEXT: v_mul_hi_u32 v4, s0, v1
+; GFX9-NEXT: v_mul_hi_u32 v6, s1, v1
+; GFX9-NEXT: v_mul_lo_u32 v1, s1, v1
+; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v3, v2
+; GFX9-NEXT: v_addc_co_u32_e32 v3, vcc, v7, v4, vcc
+; GFX9-NEXT: v_mul_lo_u32 v4, s1, v0
+; GFX9-NEXT: v_mul_hi_u32 v0, s1, v0
+; GFX9-NEXT: s_mov_b32 s3, 0x12d8fb
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_co_u32_e32 v2, vcc, v2, v4
; GFX9-NEXT: v_addc_co_u32_e32 v0, vcc, v3, v0, vcc
; GFX9-NEXT: v_addc_co_u32_e32 v2, vcc, v6, v5, vcc
; GFX9-NEXT: v_add_co_u32_e32 v0, vcc, v0, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, 0, v2, vcc
; GFX9-NEXT: v_mul_lo_u32 v1, v1, s5
; GFX9-NEXT: v_mul_hi_u32 v2, v0, s5
; GFX9-NEXT: v_mul_lo_u32 v0, v0, s5
+=======
+; GFX9-NEXT: v_addc_co_u32_e32 v1, vcc, v7, v2, vcc
+; GFX9-NEXT: v_mul_hi_u32 v2, v0, s3
+; GFX9-NEXT: v_mul_lo_u32 v1, v1, s3
+; GFX9-NEXT: v_mul_lo_u32 v0, v0, s3
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_add_u32_e32 v1, v2, v1
-; GFX9-NEXT: v_mov_b32_e32 v2, s3
-; GFX9-NEXT: v_sub_co_u32_e32 v0, vcc, s2, v0
+; GFX9-NEXT: v_sub_co_u32_e32 v0, vcc, s0, v0
+; GFX9-NEXT: v_mov_b32_e32 v2, s1
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v2, v1, vcc
+<<<<<<< HEAD
; GFX9-NEXT: v_subrev_co_u32_e32 v2, vcc, s5, v0
; GFX9-NEXT: v_subbrev_co_u32_e32 v3, vcc, 0, v1, vcc
; GFX9-NEXT: v_subrev_co_u32_e32 v4, vcc, s5, v2
; GFX9-NEXT: v_subbrev_co_u32_e32 v6, vcc, 0, v3, vcc
; GFX9-NEXT: s_mov_b32 s2, 0x12d8fa
; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s2, v2
+=======
+; GFX9-NEXT: v_subrev_co_u32_e32 v2, vcc, s3, v0
+; GFX9-NEXT: v_subbrev_co_u32_e32 v3, vcc, 0, v1, vcc
+; GFX9-NEXT: v_subrev_co_u32_e32 v4, vcc, s3, v2
+; GFX9-NEXT: v_subbrev_co_u32_e32 v6, vcc, 0, v3, vcc
+; GFX9-NEXT: s_mov_b32 s0, 0x12d8fa
+; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s0, v2
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, 0, v3
; GFX9-NEXT: v_cndmask_b32_e32 v7, -1, v7, vcc
; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v7
-; GFX9-NEXT: v_cndmask_b32_e32 v2, v2, v4, vcc
+; GFX9-NEXT: v_cmp_lt_u32_e64 s[0:1], s0, v0
; GFX9-NEXT: v_cndmask_b32_e32 v3, v3, v6, vcc
+<<<<<<< HEAD
; GFX9-NEXT: v_cmp_lt_u32_e32 vcc, s2, v0
; GFX9-NEXT: v_cndmask_b32_e64 v4, 0, -1, vcc
; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, 0, v1
; GFX9-NEXT: v_xor_b32_e32 v1, s4, v1
; GFX9-NEXT: v_mov_b32_e32 v2, s4
; GFX9-NEXT: v_subrev_co_u32_e32 v0, vcc, s4, v0
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, s[0:1]
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[0:1], 0, v1
+; GFX9-NEXT: v_cndmask_b32_e64 v6, -1, v6, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v6
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v2, v4, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v0, v0, v2, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e64 v1, v1, v3, s[0:1]
+; GFX9-NEXT: v_xor_b32_e32 v0, s2, v0
+; GFX9-NEXT: v_xor_b32_e32 v1, s2, v1
+; GFX9-NEXT: v_mov_b32_e32 v2, s2
+; GFX9-NEXT: v_subrev_co_u32_e32 v0, vcc, s2, v0
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v2, vcc
; GFX9-NEXT: global_store_dwordx2 v5, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i64_oddk_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem i64 %x, 1235195
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_mov_b32_e32 v1, s3
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
; GFX9-NEXT: s_endpgm
-;
-; GFX90A-LABEL: srem_i64_pow2k_denom:
-; GFX90A: ; %bb.0:
-; GFX90A-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
-; GFX90A-NEXT: v_mov_b32_e32 v2, 0
-; GFX90A-NEXT: s_waitcnt lgkmcnt(0)
-; GFX90A-NEXT: s_ashr_i32 s4, s3, 31
-; GFX90A-NEXT: s_lshr_b32 s4, s4, 20
-; GFX90A-NEXT: s_add_u32 s4, s2, s4
-; GFX90A-NEXT: s_addc_u32 s5, s3, 0
-; GFX90A-NEXT: s_and_b32 s4, s4, 0xfffff000
-; GFX90A-NEXT: s_sub_u32 s2, s2, s4
-; GFX90A-NEXT: s_subb_u32 s3, s3, s5
-; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[2:3], s[2:3] op_sel:[0,1]
-; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[0:1]
-; GFX90A-NEXT: s_endpgm
%r = srem i64 %x, 4096
store i64 %r, i64 addrspace(1)* %out
ret void
; GFX9-NEXT: v_cndmask_b32_e64 v7, v7, v8, s[2:3]
; GFX9-NEXT: v_subbrev_co_u32_e64 v3, s[0:1], 0, v3, s[0:1]
; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v7
-; GFX9-NEXT: v_cndmask_b32_e64 v4, v5, v4, s[0:1]
-; GFX9-NEXT: v_mov_b32_e32 v5, s7
-; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v5, v1, vcc
-; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s9, v1
-; GFX9-NEXT: v_cndmask_b32_e64 v5, 0, -1, vcc
-; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s8, v0
; GFX9-NEXT: v_cndmask_b32_e64 v3, v6, v3, s[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v6, s7
+; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v6, v1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s9, v1
; GFX9-NEXT: v_cndmask_b32_e64 v6, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s8, v0
+; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s9, v1
-; GFX9-NEXT: v_cndmask_b32_e32 v5, v5, v6, vcc
-; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v5
-; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v4, vcc
+; GFX9-NEXT: v_cndmask_b32_e32 v6, v6, v7, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v6
; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v3, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v3, v5, v4, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v3, vcc
; GFX9-NEXT: v_xor_b32_e32 v0, s10, v0
; GFX9-NEXT: v_xor_b32_e32 v1, s10, v1
; GFX9-NEXT: v_mov_b32_e32 v3, s10
; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX9-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v1, vcc, v1, v3, vcc
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl i64 4096, %y
%r = srem i64 %x, %shl.y
store i64 %r, i64 addrspace(1)* %out
; GFX9-NEXT: v_mov_b32_e32 v3, s5
; GFX9-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v2i64_pow2k_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_mov_b32_e32 v3, s5
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%r = srem <2 x i64> %x, <i64 4096, i64 4096>
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
ret void
; GFX9-NEXT: v_mul_hi_u32 v3, s12, v1
; GFX9-NEXT: v_mul_lo_u32 v4, s13, v1
; GFX9-NEXT: v_mul_lo_u32 v1, s12, v1
+<<<<<<< HEAD
; GFX9-NEXT: v_add_u32_e32 v2, v3, v2
; GFX9-NEXT: v_add_u32_e32 v2, v2, v4
; GFX9-NEXT: v_sub_u32_e32 v3, s15, v2
; GFX9-NEXT: v_addc_co_u32_e32 v8, vcc, 0, v9, vcc
; GFX9-NEXT: v_mul_lo_u32 v9, v5, v3
; GFX9-NEXT: v_mul_hi_u32 v3, v5, v3
+=======
+; GFX9-NEXT: v_mul_hi_u32 v2, s12, v0
+; GFX9-NEXT: v_mul_lo_u32 v3, s13, v0
+; GFX9-NEXT: v_mul_lo_u32 v0, s12, v0
+; GFX9-NEXT: v_add_u32_e32 v1, v2, v1
+; GFX9-NEXT: v_add_u32_e32 v1, v1, v3
+; GFX9-NEXT: v_sub_u32_e32 v2, s15, v1
+; GFX9-NEXT: v_mov_b32_e32 v3, s13
+; GFX9-NEXT: v_sub_co_u32_e32 v0, vcc, s14, v0
+; GFX9-NEXT: v_subb_co_u32_e64 v2, s[0:1], v2, v3, vcc
+; GFX9-NEXT: v_subrev_co_u32_e64 v4, s[0:1], s12, v0
+; GFX9-NEXT: v_subbrev_co_u32_e64 v7, s[2:3], 0, v2, s[0:1]
+; GFX9-NEXT: v_cmp_le_u32_e64 s[2:3], s13, v7
+; GFX9-NEXT: v_cndmask_b32_e64 v8, 0, -1, s[2:3]
+; GFX9-NEXT: v_cmp_le_u32_e64 s[2:3], s12, v4
+; GFX9-NEXT: v_cndmask_b32_e64 v9, 0, -1, s[2:3]
+; GFX9-NEXT: v_cmp_eq_u32_e64 s[2:3], s13, v7
+; GFX9-NEXT: v_cndmask_b32_e64 v8, v8, v9, s[2:3]
+; GFX9-NEXT: s_ashr_i32 s2, s11, 31
+; GFX9-NEXT: v_subb_co_u32_e64 v2, s[0:1], v2, v3, s[0:1]
+; GFX9-NEXT: s_add_u32 s10, s10, s2
+; GFX9-NEXT: v_subrev_co_u32_e64 v3, s[0:1], s12, v4
+; GFX9-NEXT: s_mov_b32 s3, s2
+; GFX9-NEXT: s_addc_u32 s11, s11, s2
+; GFX9-NEXT: s_xor_b64 s[10:11], s[10:11], s[2:3]
+; GFX9-NEXT: v_subbrev_co_u32_e64 v2, s[0:1], 0, v2, s[0:1]
+; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v8
+; GFX9-NEXT: v_cvt_f32_u32_e32 v8, s10
+; GFX9-NEXT: v_cvt_f32_u32_e32 v9, s11
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v7, v2, s[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v7, s15
+; GFX9-NEXT: v_subb_co_u32_e32 v1, vcc, v7, v1, vcc
+; GFX9-NEXT: v_mac_f32_e32 v8, s16, v9
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s13, v1
+; GFX9-NEXT: v_cndmask_b32_e64 v7, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s12, v0
+; GFX9-NEXT: v_rcp_f32_e32 v8, v8
+; GFX9-NEXT: v_cndmask_b32_e64 v10, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s13, v1
+; GFX9-NEXT: v_cndmask_b32_e32 v7, v7, v10, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v7
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v1, v2, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v2, v4, v3, s[0:1]
+; GFX9-NEXT: v_mul_f32_e32 v3, s17, v8
+; GFX9-NEXT: v_mul_f32_e32 v4, s18, v3
+; GFX9-NEXT: v_trunc_f32_e32 v4, v4
+; GFX9-NEXT: v_mac_f32_e32 v3, s19, v4
+; GFX9-NEXT: v_cvt_u32_f32_e32 v3, v3
+; GFX9-NEXT: v_cvt_u32_f32_e32 v4, v4
+; GFX9-NEXT: s_sub_u32 s2, 0, s10
+; GFX9-NEXT: s_subb_u32 s3, 0, s11
+; GFX9-NEXT: v_mul_hi_u32 v7, s2, v3
+; GFX9-NEXT: v_mul_lo_u32 v8, s2, v4
+; GFX9-NEXT: v_mul_lo_u32 v9, s3, v3
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v2, vcc
+; GFX9-NEXT: v_mul_lo_u32 v2, s2, v3
+; GFX9-NEXT: v_add_u32_e32 v7, v7, v8
+; GFX9-NEXT: v_add_u32_e32 v7, v7, v9
+; GFX9-NEXT: v_mul_lo_u32 v8, v3, v7
+; GFX9-NEXT: v_mul_hi_u32 v9, v3, v2
+; GFX9-NEXT: v_mul_hi_u32 v10, v3, v7
+; GFX9-NEXT: v_mul_hi_u32 v11, v4, v7
+; GFX9-NEXT: v_mul_lo_u32 v7, v4, v7
+; GFX9-NEXT: v_add_co_u32_e32 v8, vcc, v9, v8
+; GFX9-NEXT: v_addc_co_u32_e32 v9, vcc, 0, v10, vcc
+; GFX9-NEXT: v_mul_lo_u32 v10, v4, v2
+; GFX9-NEXT: v_mul_hi_u32 v2, v4, v2
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: s_ashr_i32 s12, s7, 31
; GFX9-NEXT: s_mov_b32 s13, s12
; GFX9-NEXT: v_add_co_u32_e32 v7, vcc, v7, v9
; GFX9-NEXT: v_cndmask_b32_e64 v9, v9, v10, s[2:3]
; GFX9-NEXT: v_subbrev_co_u32_e64 v5, s[0:1], 0, v5, s[0:1]
; GFX9-NEXT: v_cmp_ne_u32_e64 s[0:1], 0, v9
+<<<<<<< HEAD
; GFX9-NEXT: v_cndmask_b32_e64 v6, v7, v6, s[0:1]
; GFX9-NEXT: v_mov_b32_e32 v7, s7
; GFX9-NEXT: v_subb_co_u32_e32 v4, vcc, v7, v4, vcc
; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v7
; GFX9-NEXT: v_cndmask_b32_e32 v3, v3, v6, vcc
; GFX9-NEXT: v_cndmask_b32_e32 v4, v4, v5, vcc
+=======
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v8, v4, s[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v8, s7
+; GFX9-NEXT: v_subb_co_u32_e32 v3, vcc, v8, v3, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s11, v3
+; GFX9-NEXT: v_cndmask_b32_e64 v8, 0, -1, vcc
+; GFX9-NEXT: v_cmp_le_u32_e32 vcc, s10, v2
+; GFX9-NEXT: v_cndmask_b32_e64 v9, 0, -1, vcc
+; GFX9-NEXT: v_cmp_eq_u32_e32 vcc, s11, v3
+; GFX9-NEXT: v_cndmask_b32_e32 v8, v8, v9, vcc
+; GFX9-NEXT: v_cmp_ne_u32_e32 vcc, 0, v8
+; GFX9-NEXT: v_cndmask_b32_e32 v3, v3, v4, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v4, v7, v5, s[0:1]
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v2, v4, vcc
+; GFX9-NEXT: v_xor_b32_e32 v2, s12, v2
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: v_xor_b32_e32 v3, s12, v3
; GFX9-NEXT: v_xor_b32_e32 v4, s12, v4
; GFX9-NEXT: v_mov_b32_e32 v5, s12
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
; GFX9-NEXT: global_store_dwordx4 v0, v[1:4], s[4:5]
; GFX9-NEXT: s_endpgm
+<<<<<<< HEAD
;
; GFX90A-LABEL: srem_v2i64_pow2_shl_denom:
; GFX90A: ; %bb.0:
; GFX90A-NEXT: v_subb_co_u32_e32 v3, vcc, v3, v5, vcc
; GFX90A-NEXT: global_store_dwordx4 v4, v[0:3], s[8:9]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%shl.y = shl <2 x i64> <i64 4096, i64 4096>, %y
%r = srem <2 x i64> %x, %shl.y
store <2 x i64> %r, <2 x i64> addrspace(1)* %out
--- /dev/null
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=kaveri --amdhsa-code-object-version=2 -enable-ipra=0 -verify-machineinstrs < %s | FileCheck -allow-deprecated-dag-overlap -enable-var-scope -check-prefixes=GCN,CIVI %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 --amdhsa-code-object-version=2 -enable-ipra=0 -verify-machineinstrs < %s | FileCheck -allow-deprecated-dag-overlap -enable-var-scope -check-prefixes=GCN,GFX9 %s
+
+; GCN-LABEL: {{^}}use_dispatch_ptr:
+; GCN: s_load_dword s{{[0-9]+}}, s[4:5]
+define hidden void @use_dispatch_ptr() #1 {
+ %dispatch_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.dispatch.ptr() #0
+ %header_ptr = bitcast i8 addrspace(4)* %dispatch_ptr to i32 addrspace(4)*
+ %value = load volatile i32, i32 addrspace(4)* %header_ptr
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_dispatch_ptr:
+; GCN: enable_sgpr_dispatch_ptr = 1
+; GCN-NOT: s[4:5]
+; GCN-NOT: s4
+; GCN-NOT: s5
+define amdgpu_kernel void @kern_indirect_use_dispatch_ptr(i32) #1 {
+ call void @use_dispatch_ptr()
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_queue_ptr:
+; GCN: s_load_dword s{{[0-9]+}}, s[4:5]
+define hidden void @use_queue_ptr() #1 {
+ %queue_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.queue.ptr() #0
+ %header_ptr = bitcast i8 addrspace(4)* %queue_ptr to i32 addrspace(4)*
+ %value = load volatile i32, i32 addrspace(4)* %header_ptr
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_queue_ptr:
+; GCN: enable_sgpr_queue_ptr = 1
+; GCN-NOT: s[4:5]
+; GCN-NOT: s4
+; GCN-NOT: s5
+define amdgpu_kernel void @kern_indirect_use_queue_ptr(i32) #1 {
+ call void @use_queue_ptr()
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_queue_ptr_addrspacecast:
+; CIVI: s_load_dword [[APERTURE_LOAD:s[0-9]+]], s[4:5], 0x10
+; GFX9: s_getreg_b32 [[APERTURE_LOAD:s[0-9]+]]
+; CIVI: v_mov_b32_e32 v[[LO:[0-9]+]], 16
+; GCN-DAG: v_mov_b32_e32 v[[HI:[0-9]+]], [[APERTURE_LOAD]]
+; GFX9: {{flat|global}}_store_dword v{{\[[0-9]+}}:[[HI]]{{\]}}
+; CIVI: {{flat|global}}_store_dword v{{\[}}[[LO]]:[[HI]]{{\]}}
+define hidden void @use_queue_ptr_addrspacecast() #1 {
+ %asc = addrspacecast i32 addrspace(3)* inttoptr (i32 16 to i32 addrspace(3)*) to i32*
+ store volatile i32 0, i32* %asc
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_queue_ptr_addrspacecast:
+; CIVI: enable_sgpr_queue_ptr = 1
+; CIVI-NOT: s[4:5]
+; CIVI-NOT: s4
+; CIVI-NOT: s5
+define amdgpu_kernel void @kern_indirect_use_queue_ptr_addrspacecast(i32) #1 {
+ call void @use_queue_ptr_addrspacecast()
+ ret void
+}
+
+; Not really supported in callable functions.
+; GCN-LABEL: {{^}}use_kernarg_segment_ptr:
+; GCN: s_mov_b64 [[PTR:s\[[0-9]+:[0-9]+\]]], 0{{$}}
+; GCN: s_load_dword s{{[0-9]+}}, [[PTR]], 0x0{{$}}
+define hidden void @use_kernarg_segment_ptr() #1 {
+ %kernarg_segment_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.kernarg.segment.ptr() #0
+ %header_ptr = bitcast i8 addrspace(4)* %kernarg_segment_ptr to i32 addrspace(4)*
+ %value = load volatile i32, i32 addrspace(4)* %header_ptr
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_kernarg_segment_ptr:
+; GCN: enable_sgpr_kernarg_segment_ptr = 1
+define amdgpu_kernel void @kern_indirect_use_kernarg_segment_ptr(i32) #1 {
+ call void @use_kernarg_segment_ptr()
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_dispatch_id:
+; GCN: ; use s[4:5]
+define hidden void @use_dispatch_id() #1 {
+ %id = call i64 @llvm.amdgcn.dispatch.id()
+ call void asm sideeffect "; use $0", "s"(i64 %id)
+ ret void
+}
+
+; No kernarg segment so that there is a mov to check. With kernarg
+; pointer enabled, it happens to end up in the right place anyway.
+
+; GCN-LABEL: {{^}}kern_indirect_use_dispatch_id:
+; GCN: enable_sgpr_dispatch_id = 1
+; GCN-NOT: s[4:5]
+; GCN-NOT: s4
+; GCN-NOT: s5
+define amdgpu_kernel void @kern_indirect_use_dispatch_id() #1 {
+ call void @use_dispatch_id()
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_x:
+; GCN: s_waitcnt
+; GCN: ; use s4
+define hidden void @use_workgroup_id_x() #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.x()
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_stack_workgroup_id_x:
+; GCN: s_waitcnt
+; GCN-NOT: s32
+; GCN: buffer_store_dword v0, off, s[0:3], s32{{$}}
+; GCN: ; use s4
+; GCN: s_setpc_b64
+define hidden void @use_stack_workgroup_id_x() #1 {
+ %alloca = alloca i32, addrspace(5)
+ store volatile i32 0, i32 addrspace(5)* %alloca
+ %val = call i32 @llvm.amdgcn.workgroup.id.x()
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_y:
+; GCN: s_waitcnt
+; GCN: ; use s4
+define hidden void @use_workgroup_id_y() #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.y()
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_z:
+; GCN: s_waitcnt
+; GCN: ; use s4
+define hidden void @use_workgroup_id_z() #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_xy:
+; GCN: ; use s4
+; GCN: ; use s5
+define hidden void @use_workgroup_id_xy() #1 {
+ %val0 = call i32 @llvm.amdgcn.workgroup.id.x()
+ %val1 = call i32 @llvm.amdgcn.workgroup.id.y()
+ call void asm sideeffect "; use $0", "s"(i32 %val0)
+ call void asm sideeffect "; use $0", "s"(i32 %val1)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_xyz:
+; GCN: ; use s4
+; GCN: ; use s5
+; GCN: ; use s6
+define hidden void @use_workgroup_id_xyz() #1 {
+ %val0 = call i32 @llvm.amdgcn.workgroup.id.x()
+ %val1 = call i32 @llvm.amdgcn.workgroup.id.y()
+ %val2 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val0)
+ call void asm sideeffect "; use $0", "s"(i32 %val1)
+ call void asm sideeffect "; use $0", "s"(i32 %val2)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_xz:
+; GCN: ; use s4
+; GCN: ; use s5
+define hidden void @use_workgroup_id_xz() #1 {
+ %val0 = call i32 @llvm.amdgcn.workgroup.id.x()
+ %val1 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val0)
+ call void asm sideeffect "; use $0", "s"(i32 %val1)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_workgroup_id_yz:
+; GCN: ; use s4
+; GCN: ; use s5
+define hidden void @use_workgroup_id_yz() #1 {
+ %val0 = call i32 @llvm.amdgcn.workgroup.id.y()
+ %val1 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val0)
+ call void asm sideeffect "; use $0", "s"(i32 %val1)
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_x:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 0
+; GCN: enable_sgpr_workgroup_id_z = 0
+
+; GCN-NOT: s6
+; GCN: s_mov_b32 s4, s6
+; GCN: s_mov_b32 s32, 0
+; GCN: s_getpc_b64 s[6:7]
+; GCN-NEXT: s_add_u32 s6, s6, use_workgroup_id_x@rel32@lo+4
+; GCN-NEXT: s_addc_u32 s7, s7, use_workgroup_id_x@rel32@hi+12
+; GCN: s_swappc_b64
+; GCN-NEXT: s_endpgm
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_x() #1 {
+ call void @use_workgroup_id_x()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_y:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 0
+
+; GCN: s_mov_b32 s4, s7
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_y() #1 {
+ call void @use_workgroup_id_y()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_z:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 0
+; GCN: enable_sgpr_workgroup_id_z = 1
+
+; GCN: s_mov_b32 s4, s7
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_z() #1 {
+ call void @use_workgroup_id_z()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_xy:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 0
+
+; GCN: s_mov_b32 s5, s7
+; GCN: s_mov_b32 s4, s6
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_xy() #1 {
+ call void @use_workgroup_id_xy()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_xyz:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 1
+
+; GCN: s_mov_b32 s5, s7
+; GCN: s_mov_b32 s4, s6
+; GCN: s_mov_b32 s6, s8
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_xyz() #1 {
+ call void @use_workgroup_id_xyz()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_xz:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 0
+; GCN: enable_sgpr_workgroup_id_z = 1
+
+; GCN: s_mov_b32 s5, s7
+; GCN: s_mov_b32 s4, s6
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_xz() #1 {
+ call void @use_workgroup_id_xz()
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_workgroup_id_yz:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 1
+
+; GCN: s_mov_b32 s5, s8
+; GCN: s_mov_b32 s4, s7
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_workgroup_id_yz() #1 {
+ call void @use_workgroup_id_yz()
+ ret void
+}
+
+; Argument is in right place already
+; GCN-LABEL: {{^}}func_indirect_use_workgroup_id_x:
+; GCN-NOT: s4
+; GCN: v_readlane_b32 s4, v40, 0
+define hidden void @func_indirect_use_workgroup_id_x() #1 {
+ call void @use_workgroup_id_x()
+ ret void
+}
+
+; GCN-LABEL: {{^}}func_indirect_use_workgroup_id_y:
+; GCN-NOT: s4
+; GCN: v_readlane_b32 s4, v40, 0
+define hidden void @func_indirect_use_workgroup_id_y() #1 {
+ call void @use_workgroup_id_y()
+ ret void
+}
+
+; GCN-LABEL: {{^}}func_indirect_use_workgroup_id_z:
+; GCN-NOT: s4
+; GCN: v_readlane_b32 s4, v40, 0
+define hidden void @func_indirect_use_workgroup_id_z() #1 {
+ call void @use_workgroup_id_z()
+ ret void
+}
+
+; GCN-LABEL: {{^}}other_arg_use_workgroup_id_x:
+; GCN: {{flat|global}}_store_dword v{{\[[0-9]+:[0-9]+\]}}, v0
+; GCN: ; use s4
+define hidden void @other_arg_use_workgroup_id_x(i32 %arg0) #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.x()
+ store volatile i32 %arg0, i32 addrspace(1)* undef
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}other_arg_use_workgroup_id_y:
+; GCN: {{flat|global}}_store_dword v{{\[[0-9]+:[0-9]+\]}}, v0
+; GCN: ; use s4
+define hidden void @other_arg_use_workgroup_id_y(i32 %arg0) #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.y()
+ store volatile i32 %arg0, i32 addrspace(1)* undef
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}other_arg_use_workgroup_id_z:
+; GCN: {{flat|global}}_store_dword v{{\[[0-9]+:[0-9]+\]}}, v0
+; GCN: ; use s4
+define hidden void @other_arg_use_workgroup_id_z(i32 %arg0) #1 {
+ %val = call i32 @llvm.amdgcn.workgroup.id.z()
+ store volatile i32 %arg0, i32 addrspace(1)* undef
+ call void asm sideeffect "; use $0", "s"(i32 %val)
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_other_arg_use_workgroup_id_x:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 0
+; GCN: enable_sgpr_workgroup_id_z = 0
+
+; GCN-DAG: v_mov_b32_e32 v0, 0x22b
+; GCN-DAG: s_mov_b32 s4, s6
+
+; GCN-DAG: s_mov_b32 s32, 0
+; GCN-NOT: s4
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_other_arg_use_workgroup_id_x() #1 {
+ call void @other_arg_use_workgroup_id_x(i32 555)
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_other_arg_use_workgroup_id_y:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 0
+
+; GCN-DAG: v_mov_b32_e32 v0, 0x22b
+; GCN-DAG: s_mov_b32 s4, s7
+
+; GCN-DAG: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_other_arg_use_workgroup_id_y() #1 {
+ call void @other_arg_use_workgroup_id_y(i32 555)
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_other_arg_use_workgroup_id_z:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 0
+; GCN: enable_sgpr_workgroup_id_z = 1
+
+; GCN-DAG: v_mov_b32_e32 v0, 0x22b
+
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_other_arg_use_workgroup_id_z() #1 {
+ call void @other_arg_use_workgroup_id_z(i32 555)
+ ret void
+}
+
+; GCN-LABEL: {{^}}use_every_sgpr_input:
+; GCN: buffer_store_dword v{{[0-9]+}}, off, s[0:3], s32{{$}}
+; GCN: s_load_dword s{{[0-9]+}}, s[4:5]
+; GCN: s_load_dword s{{[0-9]+}}, s[6:7]
+; GCN: s_load_dword s{{[0-9]+}}, s[8:9]
+
+; GCN: ; use s[10:11]
+; GCN: ; use s12
+; GCN: ; use s13
+; GCN: ; use s14
+define hidden void @use_every_sgpr_input() #1 {
+ %alloca = alloca i32, align 4, addrspace(5)
+ store volatile i32 0, i32 addrspace(5)* %alloca
+
+ %dispatch_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.dispatch.ptr() #0
+ %dispatch_ptr.bc = bitcast i8 addrspace(4)* %dispatch_ptr to i32 addrspace(4)*
+ %val0 = load volatile i32, i32 addrspace(4)* %dispatch_ptr.bc
+
+ %queue_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.queue.ptr() #0
+ %queue_ptr.bc = bitcast i8 addrspace(4)* %queue_ptr to i32 addrspace(4)*
+ %val1 = load volatile i32, i32 addrspace(4)* %queue_ptr.bc
+
+ %implicitarg.ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.implicitarg.ptr() #0
+ %implicitarg.ptr.bc = bitcast i8 addrspace(4)* %implicitarg.ptr to i32 addrspace(4)*
+ %val2 = load volatile i32, i32 addrspace(4)* %implicitarg.ptr.bc
+
+ %val3 = call i64 @llvm.amdgcn.dispatch.id()
+ call void asm sideeffect "; use $0", "s"(i64 %val3)
+
+ %val4 = call i32 @llvm.amdgcn.workgroup.id.x()
+ call void asm sideeffect "; use $0", "s"(i32 %val4)
+
+ %val5 = call i32 @llvm.amdgcn.workgroup.id.y()
+ call void asm sideeffect "; use $0", "s"(i32 %val5)
+
+ %val6 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val6)
+
+ ret void
+}
+
+; GCN-LABEL: {{^}}kern_indirect_use_every_sgpr_input:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 1
+; GCN: enable_sgpr_workgroup_info = 0
+
+; GCN: enable_sgpr_private_segment_buffer = 1
+; GCN: enable_sgpr_dispatch_ptr = 1
+; GCN: enable_sgpr_queue_ptr = 1
+; GCN: enable_sgpr_kernarg_segment_ptr = 1
+; GCN: enable_sgpr_dispatch_id = 1
+; GCN: enable_sgpr_flat_scratch_init = 1
+
+; GCN: s_mov_b32 s13, s15
+; GCN: s_mov_b32 s12, s14
+; GCN: s_mov_b32 s14, s16
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_every_sgpr_input(i8) #1 {
+ call void @use_every_sgpr_input()
+ ret void
+}
+
+; We have to pass the kernarg segment, but there are no kernel
+; arguments so null is passed.
+; GCN-LABEL: {{^}}kern_indirect_use_every_sgpr_input_no_kernargs:
+; GCN: enable_sgpr_workgroup_id_x = 1
+; GCN: enable_sgpr_workgroup_id_y = 1
+; GCN: enable_sgpr_workgroup_id_z = 1
+; GCN: enable_sgpr_workgroup_info = 0
+
+; GCN: enable_sgpr_private_segment_buffer = 1
+; GCN: enable_sgpr_dispatch_ptr = 1
+; GCN: enable_sgpr_queue_ptr = 1
+; GCN: enable_sgpr_kernarg_segment_ptr = 0
+; GCN: enable_sgpr_dispatch_id = 1
+; GCN: enable_sgpr_flat_scratch_init = 1
+
+; GCN: s_mov_b64 s[10:11], s[8:9]
+; GCN: s_mov_b64 s[8:9], 0{{$}}
+; GCN: s_mov_b32 s32, 0
+; GCN: s_swappc_b64
+define amdgpu_kernel void @kern_indirect_use_every_sgpr_input_no_kernargs() #1 {
+ call void @use_every_sgpr_input()
+ ret void
+}
+
+; GCN-LABEL: {{^}}func_indirect_use_every_sgpr_input:
+; GCN-NOT: s6
+; GCN-NOT: s7
+; GCN-NOT: s8
+; GCN-NOT: s9
+; GCN-NOT: s10
+; GCN-NOT: s11
+; GCN-NOT: s12
+; GCN-NOT: s13
+; GCN-NOT: s[6:7]
+; GCN-NOT: s[8:9]
+; GCN-NOT: s[10:11]
+; GCN-NOT: s[12:13]
+; GCN: s_or_saveexec_b64 s[16:17], -1
+define hidden void @func_indirect_use_every_sgpr_input() #1 {
+ call void @use_every_sgpr_input()
+ ret void
+}
+
+; GCN-LABEL: {{^}}func_use_every_sgpr_input_call_use_workgroup_id_xyz:
+; GCN: s_mov_b32 s4, s12
+; GCN: s_mov_b32 s5, s13
+; GCN: s_mov_b32 s6, s14
+; GCN: ; use s[10:11]
+; GCN: ; use s12
+; GCN: ; use s13
+; GCN: ; use s14
+
+; GCN: s_swappc_b64
+define hidden void @func_use_every_sgpr_input_call_use_workgroup_id_xyz() #1 {
+ %alloca = alloca i32, align 4, addrspace(5)
+ store volatile i32 0, i32 addrspace(5)* %alloca
+
+ %dispatch_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.dispatch.ptr() #0
+ %dispatch_ptr.bc = bitcast i8 addrspace(4)* %dispatch_ptr to i32 addrspace(4)*
+ %val0 = load volatile i32, i32 addrspace(4)* %dispatch_ptr.bc
+
+ %queue_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.queue.ptr() #0
+ %queue_ptr.bc = bitcast i8 addrspace(4)* %queue_ptr to i32 addrspace(4)*
+ %val1 = load volatile i32, i32 addrspace(4)* %queue_ptr.bc
+
+ %implicitarg.ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.implicitarg.ptr() #0
+ %implicitarg.ptr.bc = bitcast i8 addrspace(4)* %implicitarg.ptr to i32 addrspace(4)*
+ %val2 = load volatile i32, i32 addrspace(4)* %implicitarg.ptr.bc
+
+ %val3 = call i64 @llvm.amdgcn.dispatch.id()
+ call void asm sideeffect "; use $0", "s"(i64 %val3)
+
+ %val4 = call i32 @llvm.amdgcn.workgroup.id.x()
+ call void asm sideeffect "; use $0", "s"(i32 %val4)
+
+ %val5 = call i32 @llvm.amdgcn.workgroup.id.y()
+ call void asm sideeffect "; use $0", "s"(i32 %val5)
+
+ %val6 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val6)
+
+ call void @use_workgroup_id_xyz()
+ ret void
+}
+
+; GCN-LABEL: {{^}}func_use_every_sgpr_input_call_use_workgroup_id_xyz_spill:
+; GCN-DAG: s_mov_b32 s33, s32
+; GCN-DAG: s_addk_i32 s32, 0x400
+; GCN-DAG: s_mov_b64 s{{\[}}[[LO_X:[0-9]+]]{{\:}}[[HI_X:[0-9]+]]{{\]}}, s[4:5]
+; GCN-DAG: s_mov_b64 s{{\[}}[[LO_Y:[0-9]+]]{{\:}}[[HI_Y:[0-9]+]]{{\]}}, s[6:7]
+
+; GCN: s_mov_b32 s4, s12
+; GCN: s_mov_b32 s5, s13
+; GCN: s_mov_b32 s6, s14
+
+; GCN-DAG: s_mov_b32 [[SAVE_Z:s[0-68-9][0-9]*]], s14
+; GCN-DAG: s_mov_b32 [[SAVE_Y:s[0-57-9][0-9]*]], s13
+; GCN-DAG: s_mov_b32 [[SAVE_X:s[0-57-9][0-9]*]], s12
+; GCN: s_mov_b64 s{{\[}}[[LO_Z:[0-9]+]]{{\:}}[[HI_Z:[0-9]+]]{{\]}}, s[8:9]
+
+; GCN: s_swappc_b64
+
+; GCN-DAG: buffer_store_dword v{{[0-9]+}}, off, s[0:3], s33{{$}}
+; GCN-DAG: s_load_dword s{{[0-9]+}}, s{{\[}}[[LO_X]]:[[HI_X]]{{\]}}, 0x0
+; GCN-DAG: s_load_dword s{{[0-9]+}}, s{{\[}}[[LO_Y]]:[[HI_Y]]{{\]}}, 0x0
+; GCN-DAG: s_load_dword s{{[0-9]+}}, s{{\[}}[[LO_Z]]:[[HI_Z]]{{\]}}, 0x0
+; GCN: ; use
+; GCN: ; use [[SAVE_X]]
+; GCN: ; use [[SAVE_Y]]
+; GCN: ; use [[SAVE_Z]]
+define hidden void @func_use_every_sgpr_input_call_use_workgroup_id_xyz_spill() #1 {
+ %alloca = alloca i32, align 4, addrspace(5)
+ call void @use_workgroup_id_xyz()
+
+ store volatile i32 0, i32 addrspace(5)* %alloca
+
+ %dispatch_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.dispatch.ptr() #0
+ %dispatch_ptr.bc = bitcast i8 addrspace(4)* %dispatch_ptr to i32 addrspace(4)*
+ %val0 = load volatile i32, i32 addrspace(4)* %dispatch_ptr.bc
+
+ %queue_ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.queue.ptr() #0
+ %queue_ptr.bc = bitcast i8 addrspace(4)* %queue_ptr to i32 addrspace(4)*
+ %val1 = load volatile i32, i32 addrspace(4)* %queue_ptr.bc
+
+ %implicitarg.ptr = call noalias i8 addrspace(4)* @llvm.amdgcn.implicitarg.ptr() #0
+ %implicitarg.ptr.bc = bitcast i8 addrspace(4)* %implicitarg.ptr to i32 addrspace(4)*
+ %val2 = load volatile i32, i32 addrspace(4)* %implicitarg.ptr.bc
+
+ %val3 = call i64 @llvm.amdgcn.dispatch.id()
+ call void asm sideeffect "; use $0", "s"(i64 %val3)
+
+ %val4 = call i32 @llvm.amdgcn.workgroup.id.x()
+ call void asm sideeffect "; use $0", "s"(i32 %val4)
+
+ %val5 = call i32 @llvm.amdgcn.workgroup.id.y()
+ call void asm sideeffect "; use $0", "s"(i32 %val5)
+
+ %val6 = call i32 @llvm.amdgcn.workgroup.id.z()
+ call void asm sideeffect "; use $0", "s"(i32 %val6)
+
+ ret void
+}
+
+declare i32 @llvm.amdgcn.workgroup.id.x() #0
+declare i32 @llvm.amdgcn.workgroup.id.y() #0
+declare i32 @llvm.amdgcn.workgroup.id.z() #0
+declare noalias i8 addrspace(4)* @llvm.amdgcn.queue.ptr() #0
+declare noalias i8 addrspace(4)* @llvm.amdgcn.kernarg.segment.ptr() #0
+declare noalias i8 addrspace(4)* @llvm.amdgcn.implicitarg.ptr() #0
+declare i64 @llvm.amdgcn.dispatch.id() #0
+declare noalias i8 addrspace(4)* @llvm.amdgcn.dispatch.ptr() #0
+
+attributes #0 = { nounwind readnone speculatable }
+attributes #1 = { nounwind noinline }
}
; GCN-LABEL: {{^}}sdiv_constant_sel_constants_i64:
-; GCN: s_cselect_b64 s[{{[0-9]+}}:{{[0-9]+}}], 0, 5
+; GCN: s_cselect_b32 s{{[0-9]+}}, 0, 5
define amdgpu_kernel void @sdiv_constant_sel_constants_i64(i64 addrspace(1)* %p, i1 %cond) {
%sel = select i1 %cond, i64 121, i64 23
%bo = sdiv i64 120, %sel
}
; GCN-LABEL: {{^}}udiv_constant_sel_constants_i64:
-; GCN: s_cselect_b64 s[{{[0-9]+}}:{{[0-9]+}}], 0, 5
+; GCN: s_cselect_b32 s{{[0-9]+}}, 0, 5
define amdgpu_kernel void @udiv_constant_sel_constants_i64(i64 addrspace(1)* %p, i1 %cond) {
%sel = select i1 %cond, i64 -4, i64 23
%bo = udiv i64 120, %sel
}
; GCN-LABEL: {{^}}srem_constant_sel_constants:
-; GCN: s_cselect_b64 s[{{[0-9]+}}:{{[0-9]+}}], 33, 3
+; GCN: s_cselect_b32 s{{[0-9]+}}, 33, 3
define amdgpu_kernel void @srem_constant_sel_constants(i64 addrspace(1)* %p, i1 %cond) {
%sel = select i1 %cond, i64 34, i64 15
%bo = srem i64 33, %sel
}
; GCN-LABEL: {{^}}urem_constant_sel_constants:
-; GCN: s_cselect_b64 s[{{[0-9]+}}:{{[0-9]+}}], 33, 3
+; GCN: s_cselect_b32 s{{[0-9]+}}, 33, 3
define amdgpu_kernel void @urem_constant_sel_constants(i64 addrspace(1)* %p, i1 %cond) {
%sel = select i1 %cond, i64 34, i64 15
%bo = urem i64 33, %sel
; GCN-LABEL: {{^}}double4_extelt:
; GCN-NOT: buffer_
-; GCN-DAG: s_mov_b32 s[[L0LO:[0-9]+]], 0x47ae147b
-; GCN-DAG: s_mov_b32 s[[L0HI:[0-9]+]], 0x3f847ae1
-; GCN-DAG: s_mov_b32 s[[L1LO:[0-9]+]], 0xc28f5c29
-; GCN-DAG: s_mov_b32 s[[L1HI:[0-9]+]], 0x3ff028f5
; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; GCN: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s{{\[}}[[L1LO]]:[[L1HI]]{{\]}}, s{{\[}}[[L0LO]]:[[L0HI]]{{\]}}
-; GCN-DAG: s_mov_b32 s[[L2LO:[0-9]+]], 0xe147ae14
-; GCN-DAG: s_mov_b32 s[[L2HI:[0-9]+]], 0x4000147a
-; GCN-DAG: s_cmp_lg_u32 [[IDX]], 2
-; GCN: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}, s{{\[}}[[L2LO]]:[[L2HI]]{{\]}}
-; GCN-DAG: s_mov_b32 s[[L3LO:[0-9]+]], 0x70a3d70a
-; GCN-DAG: s_mov_b32 s[[L3HI:[0-9]+]], 0x40100a3d
-; GCN-DAG: s_cmp_lg_u32 [[IDX]], 3
-; GCN: s_cselect_b64 s{{\[}}[[T2LO:[0-9]+]]:[[T2HI:[0-9]+]]{{\]}}, s{{\[}}[[T1LO]]:[[T1HI]]{{\]}}, s{{\[}}[[L3LO]]:[[L3HI]]{{\]}}
-; GCN-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T2LO]]
-; GCN-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T2HI]]
-; GCN: store_dwordx2 v[{{[0-9:]+}}], v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 3
+; GCN-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C3]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @double4_extelt(double addrspace(1)* %out, i32 %sel) {
entry:
%ext = extractelement <4 x double> <double 0.01, double 1.01, double 2.01, double 4.01>, i32 %sel
; GCN-LABEL: {{^}}double5_extelt:
; GCN-NOT: buffer_
-; GCN-DAG: s_mov_b32 s[[L0LO:[0-9]+]], 0x47ae147b
-; GCN-DAG: s_mov_b32 s[[L0HI:[0-9]+]], 0x3f847ae1
-; GCN-DAG: s_mov_b32 s[[L1LO:[0-9]+]], 0xc28f5c29
-; GCN-DAG: s_mov_b32 s[[L1HI:[0-9]+]], 0x3ff028f5
; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; GCN: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s{{\[}}[[L1LO]]:[[L1HI]]{{\]}}, s{{\[}}[[L0LO]]:[[L0HI]]{{\]}}
-; GCN-DAG: s_mov_b32 s[[L2LO:[0-9]+]], 0xe147ae14
-; GCN-DAG: s_mov_b32 s[[L2HI:[0-9]+]], 0x4000147a
-; GCN-DAG: s_cmp_lg_u32 [[IDX]], 2
-; GCN: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}, s{{\[}}[[L2LO]]:[[L2HI]]{{\]}}
-; GCN-DAG: s_mov_b32 s[[L3LO:[0-9]+]], 0x70a3d70a
-; GCN-DAG: s_mov_b32 s[[L3HI:[0-9]+]], 0x40100a3d
-; GCN-DAG: s_cmp_lg_u32 [[IDX]], 3
-; GCN: s_cselect_b64 s{{\[}}[[T2LO:[0-9]+]]:[[T2HI:[0-9]+]]{{\]}}, s{{\[}}[[T1LO]]:[[T1HI]]{{\]}}, s{{\[}}[[L3LO]]:[[L3HI]]{{\]}}
-; Double literals 5.01 and 4.01 share the same low 32 bits.
-; GCN-DAG: s_mov_b32 s[[L4HI:[0-9]+]], 0x40140a3d
-; GCN-DAG: s_cmp_lg_u32 [[IDX]], 4
-; GCN: s_cselect_b64 s{{\[}}[[T3LO:[0-9]+]]:[[T3HI:[0-9]+]]{{\]}}, s{{\[}}[[T2LO]]:[[T2HI]]{{\]}}, s{{\[}}[[L3LO]]:[[L4HI]]{{\]}}
-; GCN-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T3LO]]
-; GCN-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T3HI]]
-; GCN: store_dwordx2 v[{{[0-9:]+}}], v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 3
+; GCN-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 4
+; GCN-DAG: s_cselect_b64 [[C4:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C3]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C4]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @double5_extelt(double addrspace(1)* %out, i32 %sel) {
entry:
%ext = extractelement <5 x double> <double 0.01, double 1.01, double 2.01, double 4.01, double 5.01>, i32 %sel
; GCN-LABEL: {{^}}double2_extelt:
; GCN-NOT: buffer_
-; GCN-DAG: s_mov_b32 s[[L0LO:[0-9]+]], 0x47ae147b
-; GCN-DAG: s_mov_b32 s[[L0HI:[0-9]+]], 0x3f847ae1
-; GCN-DAG: s_mov_b32 s[[L1LO:[0-9]+]], 0xc28f5c29
-; GCN-DAG: s_mov_b32 s[[L1HI:[0-9]+]], 0x3ff028f5
; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; GCN: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s{{\[}}[[L1LO]]:[[L1HI]]{{\]}}, s{{\[}}[[L0LO]]:[[L0HI]]{{\]}}
-; GCN-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T0LO]]
-; GCN-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T0HI]]
-; GCN: store_dwordx2 v[{{[0-9:]+}}], v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, {{[^,]+}}, {{[^,]+}}, [[C1]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @double2_extelt(double addrspace(1)* %out, i32 %sel) {
entry:
%ext = extractelement <2 x double> <double 0.01, double 1.01>, i32 %sel
-; RUN: llc -march=amdgcn -mtriple=amdgcn-- -verify-machineinstrs < %s | FileCheck -enable-var-scope -check-prefixes=GCN,SI %s
-; RUN: llc -march=amdgcn -mtriple=amdgcn-- -mcpu=tonga -mattr=-flat-for-global -verify-machineinstrs < %s | FileCheck -enable-var-scope -check-prefixes=GCN,VI %s
+; RUN: llc -march=amdgcn -mtriple=amdgcn-- -verify-machineinstrs < %s | FileCheck -enable-var-scope -check-prefix=GCN %s
+; RUN: llc -march=amdgcn -mtriple=amdgcn-- -mcpu=tonga -mattr=-flat-for-global -verify-machineinstrs < %s | FileCheck -enable-var-scope -check-prefix=GCN %s
; GCN-LABEL: {{^}}extract_vector_elt_v3f64_2:
; GCN: buffer_load_dwordx4
; GCN-LABEL: {{^}}dyn_extract_vector_elt_v3f64:
; GCN-NOT: buffer_load
-; SI-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; SI-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 2
-; SI-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI: store_dwordx2 v[{{[0-9:]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; VI: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s[{{[0-9]+:[0-9]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 2
-; VI: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T1LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T1HI]]
-; VI: store_dwordx2 v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @dyn_extract_vector_elt_v3f64(double addrspace(1)* %out, <3 x double> %foo, i32 %elt) #0 {
%dynelt = extractelement <3 x double> %foo, i32 %elt
store volatile double %dynelt, double addrspace(1)* %out
; GCN-LABEL: {{^}}dyn_extract_vector_elt_v4f64:
; GCN-NOT: buffer_load
-; SI-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; SI-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 2
-; SI-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 3
-; SI-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
-; SI: store_dwordx2 v[{{[0-9:]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; VI: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s[{{[0-9]+:[0-9]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 2
-; VI: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 3
-; VI: s_cselect_b64 s{{\[}}[[T2LO:[0-9]+]]:[[T2HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T1LO]]:[[T1HI]]{{\]}}
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T2LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T2HI]]
-; VI: store_dwordx2 v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 3
+; GCN-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @dyn_extract_vector_elt_v4f64(double addrspace(1)* %out, <4 x double> %foo, i32 %elt) #0 {
%dynelt = extractelement <4 x double> %foo, i32 %elt
store volatile double %dynelt, double addrspace(1)* %out
-; RUN: llc -march=amdgcn -mtriple=amdgcn-- -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,SI %s
-; RUN: llc -march=amdgcn -mtriple=amdgcn-- -mcpu=tonga -mattr=-flat-for-global -verify-machineinstrs < %s | FileCheck -check-prefixes=GCN,VI %s
+; RUN: llc -march=amdgcn -mtriple=amdgcn-- -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s
+; RUN: llc -march=amdgcn -mtriple=amdgcn-- -mcpu=tonga -mattr=-flat-for-global -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s
; How the replacement of i64 stores with v2i32 stores resulted in
; breaking other users of the bitcast if they already existed
; GCN-LABEL: {{^}}dyn_extract_vector_elt_v2i64:
; GCN-NOT: buffer_load
; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; SI-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI: store_dwordx2 v[{{[0-9:]+}}]
-; VI: s_cselect_b64 s{{\[}}[[S_LO:[0-9]+]]:[[S_HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s[{{[0-9]+:[0-9]+}}]
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[S_LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[S_HI]]
-; VI: store_dwordx2 v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @dyn_extract_vector_elt_v2i64(i64 addrspace(1)* %out, <2 x i64> %foo, i32 %elt) #0 {
%dynelt = extractelement <2 x i64> %foo, i32 %elt
store volatile i64 %dynelt, i64 addrspace(1)* %out
}
; GCN-LABEL: {{^}}dyn_extract_vector_elt_v3i64:
-; SI-NOT: buffer_load
-; SI-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; SI-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 2
-; SI-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI: store_dwordx2 v[{{[0-9:]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; VI: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s[{{[0-9]+:[0-9]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 2
-; VI: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T1LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T1HI]]
-; VI: store_dwordx2 v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-NOT: buffer_load
+; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @dyn_extract_vector_elt_v3i64(i64 addrspace(1)* %out, <3 x i64> %foo, i32 %elt) #0 {
%dynelt = extractelement <3 x i64> %foo, i32 %elt
store volatile i64 %dynelt, i64 addrspace(1)* %out
; GCN-LABEL: {{^}}dyn_extract_vector_elt_v4i64:
; GCN-NOT: buffer_load
-; SI-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; SI-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 2
-; SI-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
-; SI-DAG: s_cmp_eq_u32 [[IDX]], 3
-; SI-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
-; SI-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
-; SI: store_dwordx2 v[{{[0-9:]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
-; VI: s_cselect_b64 s{{\[}}[[T0LO:[0-9]+]]:[[T0HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s[{{[0-9]+:[0-9]+}}]
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 2
-; VI: s_cselect_b64 s{{\[}}[[T1LO:[0-9]+]]:[[T1HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T0LO]]:[[T0HI]]{{\]}}
-; VI: s_cmp_eq_u32 [[IDX:s[0-9]+]], 3
-; VI: s_cselect_b64 s{{\[}}[[T2LO:[0-9]+]]:[[T2HI:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}], s{{\[}}[[T1LO]]:[[T1HI]]{{\]}}
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[T2LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[T2HI]]
-; VI: store_dwordx2 v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
+; GCN-DAG: s_cselect_b64 [[C1:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 2
+; GCN-DAG: s_cselect_b64 [[C2:[^,]+]], -1, 0
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 3
+; GCN-DAG: s_cselect_b64 [[C3:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C1]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C2]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
+; GCN-DAG: v_cndmask_b32_e{{32|64}} v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[C3]]
+; GCN: store_dwordx2 v[{{[0-9:]+}}]
define amdgpu_kernel void @dyn_extract_vector_elt_v4i64(i64 addrspace(1)* %out, <4 x i64> %foo, i32 %elt) #0 {
%dynelt = extractelement <4 x i64> %foo, i32 %elt
store volatile i64 %dynelt, i64 addrspace(1)* %out
declare void @extern_func() #0
define amdgpu_kernel void @stack_object_addrspacecast_in_kernel_no_calls() {
+<<<<<<< HEAD
; FLAT_SCR_OPT-LABEL: stack_object_addrspacecast_in_kernel_no_calls:
; FLAT_SCR_OPT: ; %bb.0:
; FLAT_SCR_OPT-NEXT: s_add_u32 s0, s0, s3
; FLAT_SCR_ARCH-NEXT: flat_store_dword v[0:1], v2
; FLAT_SCR_ARCH-NEXT: s_waitcnt_vscnt null, 0x0
; FLAT_SCR_ARCH-NEXT: s_endpgm
+=======
+; GCN-LABEL: stack_object_addrspacecast_in_kernel_no_calls:
+; GCN: ; %bb.0:
+; GCN-NEXT: s_add_u32 s0, s0, s3
+; GCN-NEXT: s_addc_u32 s1, s1, 0
+; GCN-NEXT: s_setreg_b32 hwreg(HW_REG_FLAT_SCR_LO), s0
+; GCN-NEXT: s_setreg_b32 hwreg(HW_REG_FLAT_SCR_HI), s1
+; GCN-NEXT: v_mov_b32_e32 v0, 4
+; GCN-NEXT: s_getreg_b32 s0, hwreg(HW_REG_SH_MEM_BASES, 0, 16)
+; GCN-NEXT: v_mov_b32_e32 v2, 0
+; GCN-NEXT: s_lshl_b32 s0, s0, 16
+; GCN-NEXT: v_cmp_ne_u32_e32 vcc_lo, -1, v0
+; GCN-NEXT: v_cndmask_b32_e64 v1, 0, s0, vcc_lo
+; GCN-NEXT: v_cndmask_b32_e32 v0, 0, v0, vcc_lo
+; GCN-NEXT: flat_store_dword v[0:1], v2
+; GCN-NEXT: s_waitcnt_vscnt null, 0x0
+; GCN-NEXT: s_endpgm
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%alloca = alloca i32, addrspace(5)
%cast = addrspacecast i32 addrspace(5)* %alloca to i32*
store volatile i32 0, i32* %cast
}
; GCN-LABEL: {{^}}double2_inselt:
-; GCN: s_load_dwordx4 s{{\[}}[[FIRST:[0-9]+]]:[[LAST:[0-9]+]]{{\]}}, s[{{[0-9]+:[0-9]+}}]
; GCN-NOT: v_movrel
; GCN-NOT: buffer_
-; GCN: s_cmp_lg_u32 [[IDX:s[0-9]+]], 1
-; GCN: s_cselect_b64 s{{\[}}[[P0_LO:[0-9]+]]:[[P0_HI:[0-9]+]]{{\]}}, s{{\[}}{{[0-9]+}}:[[LAST]]{{\]}}, 1.0
-; GCN: s_cmp_lg_u32 [[IDX]], 0
-; GCN: s_cselect_b64 s{{\[}}[[P1_LO:[0-9]+]]:[[P1_HI:[0-9]+]]{{\]}}, s{{\[}}[[FIRST]]:{{[0-9]+}}{{\]}}, 1.0
-; GCN: v_mov_b32_e32 v[[V_FIRST:[0-9]+]], s[[P1_LO]]
-; GCN: v_mov_b32_e32 v[[V_SECOND:[0-9]+]], s[[P1_HI]]
-; GCN: v_mov_b32_e32 v[[V_THIRD:[0-9]+]], s[[P0_LO]]
-; GCN: v_mov_b32_e32 v[[V_LAST:[0-9]+]], s[[P0_HI]]
-; GCN: flat_store_dwordx4 {{v\[[0-9]+:[0-9]+\]}}, v{{\[}}[[V_FIRST]]:[[V_LAST]]{{\]}}
+; GCN-DAG: s_cmp_eq_u32 [[IDX:s[0-9]+]], 1
+; GCN-DAG: s_cselect_b64 [[CC1:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e32 v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[CC1]]
+; GCN-DAG: v_cndmask_b32_e64 v{{[0-9]+}}, v{{[0-9]+}}, 0, [[CC1]]
+; GCN-DAG: s_cmp_eq_u32 [[IDX]], 0
+; GCN-DAG: s_cselect_b64 [[CC2:[^,]+]], -1, 0
+; GCN-DAG: v_cndmask_b32_e32 v{{[0-9]+}}, v{{[0-9]+}}, v{{[0-9]+}}, [[CC2]]
+; GCN-DAG: v_cndmask_b32_e64 v{{[0-9]+}}, v{{[0-9]+}}, 0, [[CC2]]
define amdgpu_kernel void @double2_inselt(<2 x double> addrspace(1)* %out, <2 x double> %vec, i32 %sel) {
entry:
%v = insertelement <2 x double> %vec, double 1.000000e+00, i32 %sel
; GCN-LABEL: {{^}}double5_inselt:
; GCN-NOT: v_movrel
; GCN-NOT: buffer_
-; GCN-COUNT-5: s_cselect_b64
+; GCN-COUNT-10: v_cndmask_b32
define amdgpu_kernel void @double5_inselt(<5 x double> addrspace(1)* %out, <5 x double> %vec, i32 %sel) {
entry:
%v = insertelement <5 x double> %vec, double 1.000000e+00, i32 %sel
;
; VI-LABEL: dynamic_insertelement_v2f64:
; VI: ; %bb.0:
+<<<<<<< HEAD
; VI-NEXT: s_load_dword s10, s[4:5], 0x60
; VI-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x0
; VI-NEXT: s_load_dwordx4 s[4:7], s[4:5], 0x30
; VI-NEXT: s_cselect_b64 s[6:7], s[6:7], s[8:9]
; VI-NEXT: s_cmp_lg_u32 s10, 0
; VI-NEXT: s_cselect_b64 s[4:5], s[4:5], s[8:9]
+=======
+; VI-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x0
+; VI-NEXT: s_load_dwordx4 s[8:11], s[4:5], 0x30
+; VI-NEXT: s_load_dword s4, s[4:5], 0x60
+; VI-NEXT: v_mov_b32_e32 v1, 0x40200000
+; VI-NEXT: s_mov_b32 s3, 0x1100f000
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; VI-NEXT: s_mov_b32 s2, -1
-; VI-NEXT: v_mov_b32_e32 v0, s4
-; VI-NEXT: v_mov_b32_e32 v1, s5
-; VI-NEXT: v_mov_b32_e32 v2, s6
-; VI-NEXT: v_mov_b32_e32 v3, s7
+; VI-NEXT: s_waitcnt lgkmcnt(0)
+; VI-NEXT: v_mov_b32_e32 v0, s11
+; VI-NEXT: s_cmp_eq_u32 s4, 1
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_cndmask_b32_e32 v3, v0, v1, vcc
+; VI-NEXT: v_mov_b32_e32 v0, s10
+; VI-NEXT: s_cmp_eq_u32 s4, 0
+; VI-NEXT: v_cndmask_b32_e64 v2, v0, 0, vcc
+; VI-NEXT: v_mov_b32_e32 v0, s9
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_cndmask_b32_e32 v1, v0, v1, vcc
+; VI-NEXT: v_mov_b32_e32 v0, s8
+; VI-NEXT: v_cndmask_b32_e64 v0, v0, 0, vcc
; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[0:3], 0
; VI-NEXT: s_endpgm
%vecins = insertelement <2 x double> %a, double 8.0, i32 %b
; VI-NEXT: s_mov_b32 s7, 0x1100f000
; VI-NEXT: s_mov_b32 s6, -1
; VI-NEXT: s_waitcnt lgkmcnt(0)
+<<<<<<< HEAD
; VI-NEXT: s_cmp_lg_u32 s8, 1
; VI-NEXT: s_cselect_b64 s[2:3], s[2:3], 5
; VI-NEXT: s_cmp_lg_u32 s8, 0
; VI-NEXT: v_mov_b32_e32 v2, s2
; VI-NEXT: v_mov_b32_e32 v3, s3
; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[4:7], 0
+=======
+; VI-NEXT: s_cmp_eq_u32 s6, 1
+; VI-NEXT: s_cselect_b64 s[4:5], -1, 0
+; VI-NEXT: v_mov_b32_e32 v0, s11
+; VI-NEXT: v_cndmask_b32_e64 v3, v0, 0, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v0, s10
+; VI-NEXT: s_cmp_eq_u32 s6, 0
+; VI-NEXT: v_cndmask_b32_e64 v2, v0, 5, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v0, s9
+; VI-NEXT: s_cselect_b64 s[4:5], -1, 0
+; VI-NEXT: v_cndmask_b32_e64 v1, v0, 0, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v0, s8
+; VI-NEXT: v_cndmask_b32_e64 v0, v0, 5, s[4:5]
+; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[0:3], 0
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; VI-NEXT: s_endpgm
%vecins = insertelement <2 x i64> %a, i64 5, i32 %b
store <2 x i64> %vecins, <2 x i64> addrspace(1)* %out, align 8
; VI-NEXT: s_load_dwordx2 s[4:5], s[4:5], 0x30
; VI-NEXT: s_mov_b32 s3, 0x1100f000
; VI-NEXT: s_waitcnt lgkmcnt(0)
+<<<<<<< HEAD
; VI-NEXT: s_cmp_lg_u32 s12, 1
; VI-NEXT: s_mov_b32 s2, -1
; VI-NEXT: s_cselect_b64 s[6:7], s[10:11], 5
; VI-NEXT: v_mov_b32_e32 v1, s9
; VI-NEXT: v_mov_b32_e32 v2, s6
; VI-NEXT: v_mov_b32_e32 v3, s7
+=======
+; VI-NEXT: v_mov_b32_e32 v0, s11
+; VI-NEXT: s_cmp_eq_u32 s12, 1
+; VI-NEXT: s_cselect_b64 s[4:5], -1, 0
+; VI-NEXT: v_cndmask_b32_e64 v3, v0, 0, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v0, s10
+; VI-NEXT: s_cmp_eq_u32 s12, 0
+; VI-NEXT: v_cndmask_b32_e64 v2, v0, 5, s[4:5]
+; VI-NEXT: s_cselect_b64 s[4:5], -1, 0
+; VI-NEXT: v_mov_b32_e32 v0, s9
+; VI-NEXT: v_cndmask_b32_e64 v1, v0, 0, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v0, s8
+; VI-NEXT: s_cmp_eq_u32 s12, 2
+; VI-NEXT: v_cndmask_b32_e64 v0, v0, 5, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v4, s7
+; VI-NEXT: s_cselect_b64 s[4:5], -1, 0
+; VI-NEXT: v_cndmask_b32_e64 v5, v4, 0, s[4:5]
+; VI-NEXT: v_mov_b32_e32 v4, s6
+; VI-NEXT: v_cndmask_b32_e64 v4, v4, 5, s[4:5]
+; VI-NEXT: buffer_store_dwordx2 v[4:5], off, s[0:3], 0 offset:16
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[0:3], 0
; VI-NEXT: s_endpgm
%vecins = insertelement <3 x i64> %a, i64 5, i32 %b
;
; VI-LABEL: dynamic_insertelement_v4f64:
; VI: ; %bb.0:
+<<<<<<< HEAD
; VI-NEXT: s_load_dword s16, s[4:5], 0x40
; VI-NEXT: s_load_dwordx8 s[8:15], s[4:5], 0x20
; VI-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x0
; VI-NEXT: s_mov_b32 s4, 0
; VI-NEXT: s_mov_b32 s5, 0x40200000
+=======
+; VI-NEXT: s_load_dwordx2 s[0:1], s[4:5], 0x0
+; VI-NEXT: s_load_dwordx8 s[8:15], s[4:5], 0x20
+; VI-NEXT: s_load_dword s4, s[4:5], 0x40
+; VI-NEXT: v_mov_b32_e32 v4, 0x40200000
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; VI-NEXT: s_mov_b32 s3, 0x1100f000
-; VI-NEXT: s_waitcnt lgkmcnt(0)
-; VI-NEXT: s_cmp_lg_u32 s16, 1
-; VI-NEXT: s_cselect_b64 s[6:7], s[10:11], s[4:5]
-; VI-NEXT: s_cmp_lg_u32 s16, 0
-; VI-NEXT: s_cselect_b64 s[8:9], s[8:9], s[4:5]
-; VI-NEXT: s_cmp_lg_u32 s16, 3
-; VI-NEXT: s_cselect_b64 s[10:11], s[14:15], s[4:5]
-; VI-NEXT: s_cmp_lg_u32 s16, 2
-; VI-NEXT: s_cselect_b64 s[4:5], s[12:13], s[4:5]
; VI-NEXT: s_mov_b32 s2, -1
-; VI-NEXT: v_mov_b32_e32 v0, s4
-; VI-NEXT: v_mov_b32_e32 v1, s5
-; VI-NEXT: v_mov_b32_e32 v2, s10
-; VI-NEXT: v_mov_b32_e32 v3, s11
-; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[0:3], 0 offset:16
-; VI-NEXT: s_nop 0
+; VI-NEXT: s_waitcnt lgkmcnt(0)
+; VI-NEXT: v_mov_b32_e32 v0, s11
+; VI-NEXT: s_cmp_eq_u32 s4, 1
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_cndmask_b32_e32 v3, v0, v4, vcc
+; VI-NEXT: v_mov_b32_e32 v0, s10
+; VI-NEXT: s_cmp_eq_u32 s4, 0
+; VI-NEXT: v_cndmask_b32_e64 v2, v0, 0, vcc
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_mov_b32_e32 v0, s9
+; VI-NEXT: v_cndmask_b32_e32 v1, v0, v4, vcc
; VI-NEXT: v_mov_b32_e32 v0, s8
-; VI-NEXT: v_mov_b32_e32 v1, s9
-; VI-NEXT: v_mov_b32_e32 v2, s6
-; VI-NEXT: v_mov_b32_e32 v3, s7
+; VI-NEXT: s_cmp_eq_u32 s4, 3
+; VI-NEXT: v_cndmask_b32_e64 v0, v0, 0, vcc
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_mov_b32_e32 v5, s15
+; VI-NEXT: v_cndmask_b32_e32 v7, v5, v4, vcc
+; VI-NEXT: v_mov_b32_e32 v5, s14
+; VI-NEXT: s_cmp_eq_u32 s4, 2
+; VI-NEXT: v_cndmask_b32_e64 v6, v5, 0, vcc
+; VI-NEXT: v_mov_b32_e32 v5, s13
+; VI-NEXT: s_cselect_b64 vcc, -1, 0
+; VI-NEXT: v_cndmask_b32_e32 v5, v5, v4, vcc
+; VI-NEXT: v_mov_b32_e32 v4, s12
+; VI-NEXT: v_cndmask_b32_e64 v4, v4, 0, vcc
+; VI-NEXT: buffer_store_dwordx4 v[4:7], off, s[0:3], 0 offset:16
; VI-NEXT: buffer_store_dwordx4 v[0:3], off, s[0:3], 0
; VI-NEXT: s_endpgm
%vecins = insertelement <4 x double> %a, double 8.0, i32 %b
; GFX9: ; %bb.0: ; %bb
; GFX9-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
; GFX9-NEXT: s_waitcnt lgkmcnt(0)
+<<<<<<< HEAD
; GFX9-NEXT: s_mul_i32 s9, s0, s3
; GFX9-NEXT: s_mul_hi_u32 s10, s0, s2
; GFX9-NEXT: s_mul_hi_u32 s5, s0, s3
; GFX9-NEXT: s_addc_u32 s5, 0, s5
; GFX9-NEXT: s_sub_u32 s6, s4, s2
; GFX9-NEXT: s_subb_u32 s7, s5, 0
+=======
+; GFX9-NEXT: s_mul_i32 s7, s0, s3
+; GFX9-NEXT: s_mul_hi_u32 s8, s0, s2
+; GFX9-NEXT: s_mul_hi_u32 s6, s0, s3
+; GFX9-NEXT: s_add_u32 s9, s8, s7
+; GFX9-NEXT: s_mul_i32 s5, s1, s2
+; GFX9-NEXT: s_addc_u32 s6, 0, s6
+; GFX9-NEXT: s_add_u32 s9, s9, s5
+; GFX9-NEXT: s_mul_hi_u32 s4, s1, s2
+; GFX9-NEXT: s_mul_hi_i32 s10, s1, s3
+; GFX9-NEXT: s_addc_u32 s4, s6, s4
+; GFX9-NEXT: s_addc_u32 s6, s10, 0
+; GFX9-NEXT: s_mul_i32 s9, s1, s3
+; GFX9-NEXT: s_add_u32 s4, s4, s9
+; GFX9-NEXT: s_addc_u32 s6, 0, s6
+; GFX9-NEXT: s_sub_u32 s9, s4, s2
+; GFX9-NEXT: s_subb_u32 s10, s6, 0
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX9-NEXT: s_cmp_lt_i32 s1, 0
-; GFX9-NEXT: s_cselect_b64 s[4:5], s[6:7], s[4:5]
-; GFX9-NEXT: s_sub_u32 s6, s4, s0
-; GFX9-NEXT: s_subb_u32 s7, s5, 0
+; GFX9-NEXT: s_cselect_b64 vcc, -1, 0
+; GFX9-NEXT: v_mov_b32_e32 v0, s6
+; GFX9-NEXT: v_mov_b32_e32 v1, s10
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v0, v1, vcc
+; GFX9-NEXT: v_mov_b32_e32 v1, s4
+; GFX9-NEXT: v_mov_b32_e32 v2, s9
+; GFX9-NEXT: v_cndmask_b32_e32 v2, v1, v2, vcc
+; GFX9-NEXT: v_subrev_co_u32_e32 v3, vcc, s0, v2
+; GFX9-NEXT: v_subbrev_co_u32_e32 v1, vcc, 0, v0, vcc
; GFX9-NEXT: s_cmp_lt_i32 s3, 0
-; GFX9-NEXT: s_cselect_b64 s[4:5], s[6:7], s[4:5]
-; GFX9-NEXT: s_add_i32 s1, s10, s9
-; GFX9-NEXT: s_add_i32 s1, s1, s8
-; GFX9-NEXT: s_ashr_i32 s6, s1, 31
-; GFX9-NEXT: s_mov_b32 s7, s6
-; GFX9-NEXT: s_cmp_lg_u64 s[4:5], s[6:7]
-; GFX9-NEXT: s_mul_i32 s2, s0, s2
-; GFX9-NEXT: v_mov_b32_e32 v0, s1
-; GFX9-NEXT: s_cselect_b64 s[0:1], -1, 0
-; GFX9-NEXT: v_cndmask_b32_e64 v1, v0, 0, s[0:1]
-; GFX9-NEXT: v_mov_b32_e32 v0, s2
-; GFX9-NEXT: v_cndmask_b32_e64 v0, v0, 0, s[0:1]
+; GFX9-NEXT: s_cselect_b64 vcc, -1, 0
+; GFX9-NEXT: s_add_i32 s1, s8, s7
+; GFX9-NEXT: s_add_i32 s1, s1, s5
+; GFX9-NEXT: s_ashr_i32 s4, s1, 31
+; GFX9-NEXT: v_cndmask_b32_e32 v1, v0, v1, vcc
+; GFX9-NEXT: v_cndmask_b32_e32 v0, v2, v3, vcc
+; GFX9-NEXT: s_mov_b32 s5, s4
+; GFX9-NEXT: s_mul_i32 s0, s0, s2
+; GFX9-NEXT: v_cmp_ne_u64_e32 vcc, s[4:5], v[0:1]
+; GFX9-NEXT: v_mov_b32_e32 v0, s0
+; GFX9-NEXT: v_mov_b32_e32 v2, s1
+; GFX9-NEXT: v_cndmask_b32_e64 v1, v2, 0, vcc
+; GFX9-NEXT: v_cndmask_b32_e64 v0, v0, 0, vcc
; GFX9-NEXT: global_store_dwordx2 v[0:1], v[0:1], off
; GFX9-NEXT: s_endpgm
;
; GFX10: ; %bb.0: ; %bb
; GFX10-NEXT: s_load_dwordx4 s[0:3], s[0:1], 0x24
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
+<<<<<<< HEAD
; GFX10-NEXT: s_mul_i32 s9, s0, s3
; GFX10-NEXT: s_mul_hi_u32 s10, s0, s2
; GFX10-NEXT: s_mul_hi_u32 s5, s0, s3
; GFX10-NEXT: s_addc_u32 s5, 0, s5
; GFX10-NEXT: s_sub_u32 s6, s4, s2
; GFX10-NEXT: s_subb_u32 s7, s5, 0
+=======
+; GFX10-NEXT: s_mul_i32 s7, s0, s3
+; GFX10-NEXT: s_mul_hi_u32 s8, s0, s2
+; GFX10-NEXT: s_mul_hi_u32 s6, s0, s3
+; GFX10-NEXT: s_add_u32 s11, s8, s7
+; GFX10-NEXT: s_mul_i32 s5, s1, s2
+; GFX10-NEXT: s_addc_u32 s6, 0, s6
+; GFX10-NEXT: s_mul_hi_u32 s4, s1, s2
+; GFX10-NEXT: s_add_u32 s11, s11, s5
+; GFX10-NEXT: s_mul_hi_i32 s9, s1, s3
+; GFX10-NEXT: s_addc_u32 s4, s6, s4
+; GFX10-NEXT: s_mul_i32 s10, s1, s3
+; GFX10-NEXT: s_addc_u32 s6, s9, 0
+; GFX10-NEXT: s_add_u32 s4, s4, s10
+; GFX10-NEXT: s_addc_u32 s6, 0, s6
+; GFX10-NEXT: s_sub_u32 s9, s4, s2
+; GFX10-NEXT: s_subb_u32 s10, s6, 0
+; GFX10-NEXT: v_mov_b32_e32 v1, s9
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
; GFX10-NEXT: s_cmp_lt_i32 s1, 0
-; GFX10-NEXT: s_cselect_b64 s[4:5], s[6:7], s[4:5]
-; GFX10-NEXT: s_sub_u32 s6, s4, s0
-; GFX10-NEXT: s_subb_u32 s7, s5, 0
+; GFX10-NEXT: v_mov_b32_e32 v0, s10
+; GFX10-NEXT: s_cselect_b32 vcc_lo, -1, 0
; GFX10-NEXT: s_cmp_lt_i32 s3, 0
+; GFX10-NEXT: v_cndmask_b32_e32 v2, s4, v1, vcc_lo
+; GFX10-NEXT: v_cndmask_b32_e32 v0, s6, v0, vcc_lo
+; GFX10-NEXT: v_sub_co_u32 v3, vcc_lo, v2, s0
; GFX10-NEXT: s_mul_i32 s0, s0, s2
-; GFX10-NEXT: s_cselect_b64 s[4:5], s[6:7], s[4:5]
-; GFX10-NEXT: s_add_i32 s1, s10, s9
-; GFX10-NEXT: s_add_i32 s1, s1, s8
-; GFX10-NEXT: s_ashr_i32 s6, s1, 31
-; GFX10-NEXT: s_mov_b32 s7, s6
-; GFX10-NEXT: s_cmp_lg_u64 s[4:5], s[6:7]
-; GFX10-NEXT: s_cselect_b32 s2, -1, 0
-; GFX10-NEXT: v_cndmask_b32_e64 v1, s1, 0, s2
-; GFX10-NEXT: v_cndmask_b32_e64 v0, s0, 0, s2
+; GFX10-NEXT: v_subrev_co_ci_u32_e32 v1, vcc_lo, 0, v0, vcc_lo
+; GFX10-NEXT: s_cselect_b32 vcc_lo, -1, 0
+; GFX10-NEXT: s_add_i32 s1, s8, s7
+; GFX10-NEXT: s_add_i32 s1, s1, s5
+; GFX10-NEXT: v_cndmask_b32_e32 v1, v0, v1, vcc_lo
+; GFX10-NEXT: v_cndmask_b32_e32 v0, v2, v3, vcc_lo
+; GFX10-NEXT: s_ashr_i32 s4, s1, 31
+; GFX10-NEXT: s_mov_b32 s5, s4
+; GFX10-NEXT: v_cmp_ne_u64_e32 vcc_lo, s[4:5], v[0:1]
+; GFX10-NEXT: v_cndmask_b32_e64 v1, s1, 0, vcc_lo
+; GFX10-NEXT: v_cndmask_b32_e64 v0, s0, 0, vcc_lo
; GFX10-NEXT: global_store_dwordx2 v[0:1], v[0:1], off
; GFX10-NEXT: s_endpgm
bb:
; GCN: s_load_dwordx2
; GCN: s_cmp_eq_u32
-; GCN: s_cselect_b64
+; GCN: v_cndmask_b32
+; GCN: v_cndmask_b32
; GCN-NOT: load_dword
; GCN: flat_load_dwordx2
; GCN: s_load_dwordx2
; GCN: s_load_dwordx2 s{{\[[0-9]+:[0-9]+\]}}, s{{\[[0-9]+:[0-9]+\]}}, 0x0{{$}}
; GCN: s_load_dwordx2 s{{\[[0-9]+:[0-9]+\]}}, s{{\[[0-9]+:[0-9]+\]}}, 0x0{{$}}
-; GCN: s_cselect_b64
+; GCN: v_cndmask_b32
+; GCN: v_cndmask_b32
; GCN: flat_store_dwordx2
define amdgpu_kernel void @select_ptr_crash_i64_global(i32 %tmp, [8 x i32], i64 addrspace(1)* %ptr0, [8 x i32], i64 addrspace(1)* %ptr1, [8 x i32], i64 addrspace(1)* %ptr2) {
%tmp2 = icmp eq i32 %tmp, 0
--- /dev/null
+; RUN: opt -S -mtriple=amdgcn-- -amdgpu-lower-module-lds < %s | FileCheck %s
+; RUN: opt -S -mtriple=amdgcn-- -passes=amdgpu-lower-module-lds < %s | FileCheck %s
+
+;.
+; Kernel LDS lowering.
+;.
+; @lds.1: is part of @llvm.used list, and also it is used within kernel, hence it is lowered.
+; @lds.2: is part of @llvm.compiler.used list, and also it is used within kernel, hence it is lowered.
+; @lds.3: is used as initializer to @gptr.3, hence @lds.3 is not lowered, though it is used within kernel.
+; @lds.4: is used as initializer to @gptr.4, hence @lds.4 is not lowered, though it is used within kernel,
+; irrespective of the uses of @gptr.4 itself ( @gptr.4 is part of llvm.compiler.used list ).
+; @lds.5: is part of @llvm.used list, but is not used within kernel, hence it is not lowered.
+; @lds.6: is part of @llvm.compiler.used list, but is not used within kernel, hence it is not lowered.
+;.
+
+; CHECK: %llvm.amdgcn.kernel.k0.lds.t = type { i32, i16 }
+
+; CHECK-NOT: @lds.1
+; CHECK-NOT: @lds.2
+; CHECK: @lds.3 = addrspace(3) global i64 undef, align 8
+; CHECK: @lds.4 = addrspace(3) global float undef, align 4
+; CHECK: @lds.5 = addrspace(3) global i16 undef, align 2
+; CHECK: @lds.6 = addrspace(3) global i32 undef, align 4
+@lds.1 = addrspace(3) global i16 undef, align 2
+@lds.2 = addrspace(3) global i32 undef, align 4
+@lds.3 = addrspace(3) global i64 undef, align 8
+@lds.4 = addrspace(3) global float undef, align 4
+@lds.5 = addrspace(3) global i16 undef, align 2
+@lds.6 = addrspace(3) global i32 undef, align 4
+
+; CHECK: @gptr.3 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* @lds.3 to i64*), align 8
+; CHECK: @gptr.4 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast (float addrspace(3)* @lds.4 to i64 addrspace(3)*) to i64*), align 8
+@gptr.3 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* @lds.3 to i64*), align 8
+@gptr.4 = addrspace(1) global i64* addrspacecast (float addrspace(3)* @lds.4 to i64*), align 8
+
+; CHECK: @llvm.amdgcn.kernel.k0.lds = internal addrspace(3) global %llvm.amdgcn.kernel.k0.lds.t undef, align 4
+
+; CHECK: @llvm.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i16 addrspace(3)* @lds.5 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+; CHECK: @llvm.compiler.used = appending global [2 x i8*] [i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.4 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @lds.6 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+@llvm.used = appending global [2 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i16 addrspace(3)* @lds.1 to i8 addrspace(3)*) to i8*), i8* addrspacecast (i8 addrspace(3)* bitcast (i16 addrspace(3)* @lds.5 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+@llvm.compiler.used = appending global [3 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @lds.2 to i8 addrspace(3)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.4 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @lds.6 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+
+; CHECK-LABEL: @k0()
+; CHECK: %ld.lds.1 = load i16, i16 addrspace(3)* getelementptr inbounds (%llvm.amdgcn.kernel.k0.lds.t, %llvm.amdgcn.kernel.k0.lds.t addrspace(3)* @llvm.amdgcn.kernel.k0.lds, i32 0, i32 1), align 4
+; CHECK: %ld.lds.2 = load i32, i32 addrspace(3)* getelementptr inbounds (%llvm.amdgcn.kernel.k0.lds.t, %llvm.amdgcn.kernel.k0.lds.t addrspace(3)* @llvm.amdgcn.kernel.k0.lds, i32 0, i32 0), align 4
+; CHECK: %ld.lds.3 = load i64, i64 addrspace(3)* @lds.3, align 4
+; CHECK: %ld.lds.4 = load float, float addrspace(3)* @lds.4, align 4
+; CHECK: ret void
+define amdgpu_kernel void @k0() {
+ %ld.lds.1 = load i16, i16 addrspace(3)* @lds.1
+ %ld.lds.2 = load i32, i32 addrspace(3)* @lds.2
+ %ld.lds.3 = load i64, i64 addrspace(3)* @lds.3
+ %ld.lds.4 = load float, float addrspace(3)* @lds.4
+ ret void
+}
--- /dev/null
+; RUN: opt -S -mtriple=amdgcn-- -amdgpu-lower-module-lds < %s | FileCheck %s
+; RUN: opt -S -mtriple=amdgcn-- -passes=amdgpu-lower-module-lds < %s | FileCheck %s
+
+;.
+; @lds.1: is aliased with @alias.to.lds.1, and @alias.to.lds.1 is used within kernel @k0.
+; Hence, @lds.1 is lowered.
+; @lds.2: is aliased with @alias.to.lds.2, and @alias.to.lds.2 is used within non-kernel @f0,
+; Hence, @lds.2 is lowered.
+; @lds.3: is used as initializer to global @gptr.3, and @gptr.3 is aliased with @alias.to.gptr.3,
+; and @alias.to.gptr.3 is used within kernel @k1. Hence, @lds.3 is lowered.
+; @lds.4: is used as initializer to global @gptr.4, and @gptr.4 is aliased with @alias.to.gptr.4,
+; and @alias.to.gptr.4 is used within non-kernel @f1. Hence, @lds.4 is lowered.
+; @lds.5: is aliased with @alias.to.lds.5, but neither @lds.5 nor @alias.to.lds.5 is used anywhere.
+; Hence, @lds.5 is not lowered.
+; @lds.6: is used as initializer to global @gptr.6, and @gptr.6 is aliased with @alias.to.gptr.6.
+; But none of them are used anywhere. Hence, @lds.6 is not lowered.
+;.
+
+; CHECK: %llvm.amdgcn.module.lds.t = type { [4 x i8], [3 x i8], [1 x i8], [2 x i8] }
+
+; CHECK-NOT: @lds.1
+; CHECK-NOT: @lds.2
+; CHECK-NOT: @lds.3
+; CHECK-NOT: @lds.4
+; CHECK: @lds.5 = internal unnamed_addr addrspace(3) global [5 x i8] undef, align 8
+; CHECK: @lds.6 = internal unnamed_addr addrspace(3) global [6 x i8] undef, align 8
+@lds.1 = internal unnamed_addr addrspace(3) global [1 x i8] undef, align 1
+@lds.2 = internal unnamed_addr addrspace(3) global [2 x i8] undef, align 2
+@lds.3 = internal unnamed_addr addrspace(3) global [3 x i8] undef, align 4
+@lds.4 = internal unnamed_addr addrspace(3) global [4 x i8] undef, align 4
+@lds.5 = internal unnamed_addr addrspace(3) global [5 x i8] undef, align 8
+@lds.6 = internal unnamed_addr addrspace(3) global [6 x i8] undef, align 8
+
+; CHECK: @gptr.3 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast ([3 x i8] addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 1) to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.4 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast (%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.6 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast ([6 x i8] addrspace(3)* @lds.6 to i64 addrspace(3)*) to i64*), align 8
+@gptr.3 = addrspace(1) global i64* addrspacecast ([3 x i8] addrspace(3)* @lds.3 to i64*), align 8
+@gptr.4 = addrspace(1) global i64* addrspacecast ([4 x i8] addrspace(3)* @lds.4 to i64*), align 8
+@gptr.6 = addrspace(1) global i64* addrspacecast ([6 x i8] addrspace(3)* @lds.6 to i64*), align 8
+
+; CHECK: @llvm.amdgcn.module.lds = internal addrspace(3) global %llvm.amdgcn.module.lds.t undef, align 4
+; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 0, i32 0) to i8*)], section "llvm.metadata"
+
+; CHECK: @alias.to.lds.1 = alias [1 x i8], getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 2)
+; CHECK: @alias.to.lds.2 = alias [2 x i8], getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 3)
+; CHECK: @alias.to.gptr.3 = alias i64*, i64* addrspace(1)* @gptr.3
+; CHECK: @alias.to.gptr.4 = alias i64*, i64* addrspace(1)* @gptr.4
+; CHECK: @alias.to.lds.5 = alias [5 x i8], [5 x i8] addrspace(3)* @lds.5
+; CHECK: @alias.to.gptr.6 = alias i64*, i64* addrspace(1)* @gptr.6
+@alias.to.lds.1 = alias [1 x i8], [1 x i8] addrspace(3)* @lds.1
+@alias.to.lds.2 = alias [2 x i8], [2 x i8] addrspace(3)* @lds.2
+@alias.to.gptr.3 = alias i64*, i64* addrspace(1)* @gptr.3
+@alias.to.gptr.4 = alias i64*, i64* addrspace(1)* @gptr.4
+@alias.to.lds.5 = alias [5 x i8], [5 x i8] addrspace(3)* @lds.5
+@alias.to.gptr.6 = alias i64*, i64* addrspace(1)* @gptr.6
+
+; CHECK-LABEL: @f1
+; CHECK: %ld = load i64*, i64* addrspace(1)* @alias.to.gptr.4, align 8
+; CHECK: ret void
+define void @f1() {
+ %ld = load i64*, i64* addrspace(1)* @alias.to.gptr.4
+ ret void
+}
+
+; CHECK-LABEL: @f0
+; CHECK: %bc = bitcast [2 x i8] addrspace(3)* @alias.to.lds.2 to i8 addrspace(3)*
+; CHECK: store i8 1, i8 addrspace(3)* %bc, align 2
+; CHECK: ret void
+define void @f0() {
+ %bc = bitcast [2 x i8] addrspace(3)* @alias.to.lds.2 to i8 addrspace(3)*
+ store i8 1, i8 addrspace(3)* %bc, align 2
+ ret void
+}
+
+; CHECK-LABEL: @k1
+; CHECK-LABEL: call void @llvm.donothing() [ "ExplicitUse"(%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds) ]
+; CHECK-LABEL: %ld = load i64*, i64* addrspace(1)* @alias.to.gptr.3, align 8
+; CHECK-LABEL: ret void
+define amdgpu_kernel void @k1() {
+ %ld = load i64*, i64* addrspace(1)* @alias.to.gptr.3
+ ret void
+}
+
+; CHECK-LABEL: @k0
+; CHECK-LABEL: call void @llvm.donothing() [ "ExplicitUse"(%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds) ]
+; CHECK-LABEL: %bc = bitcast [1 x i8] addrspace(3)* @alias.to.lds.1 to i8 addrspace(3)*
+; CHECK-LABEL: store i8 1, i8 addrspace(3)* %bc, align 1
+; CHECK-LABEL: ret void
+define amdgpu_kernel void @k0() {
+ %bc = bitcast [1 x i8] addrspace(3)* @alias.to.lds.1 to i8 addrspace(3)*
+ store i8 1, i8 addrspace(3)* %bc, align 1
+ ret void
+}
--- /dev/null
+; RUN: opt -S -mtriple=amdgcn-- -amdgpu-lower-module-lds < %s | FileCheck %s
+; RUN: opt -S -mtriple=amdgcn-- -passes=amdgpu-lower-module-lds < %s | FileCheck %s
+
+;.
+; @lds.1: is part of @llvm.used list, and is no-where used. Hence it is not lowered.
+; @lds.2: is part of @llvm.compiler.used list, and is no-where used. Hence it is not lowered.
+; @lds.3: is used as initializer to @gptr.3, and is no-where used. @gptr.3 itself is also not
+; used anywhere else, hence @lds.3 is not lowered.
+; @lds.4: is used as initializer to @gptr.4, and is no-where used. @gptr.4 is part of
+; @llvm.compiler.used list, but is no-where else used. hence @lds.4 is not lowered.
+;
+; @lds.5: is used as initializer to @gptr.5, and is no-where used. @gptr.5 is part of
+; @llvm.compiler.used list, but is also used within kernel @k0. Hence @lds.5 is lowered.
+; @lds.6: is used as initializer to @gptr.6, and is no-where used. @gptr.6 is part of
+; @llvm.compiler.used list, but is also used within non-kernel function @f0. Hence @lds.6 is lowered.
+; @lds.7: is used as initializer to @gptr.7, and is no-where used. @gptr.7 is used as initializer to @gptr.8,
+; and @gptr.8 is used within non-kernel function @f1. Hence @lds.7 is lowered.
+;.
+
+; CHECK: %llvm.amdgcn.module.lds.t = type { [3 x float], [1 x float], [2 x float] }
+
+; CHECK: @lds.1 = addrspace(3) global i16 undef, align 2
+; CHECK: @lds.2 = addrspace(3) global i32 undef, align 4
+; CHECK: @lds.3 = addrspace(3) global i64 undef, align 8
+; CHECK: @lds.4 = addrspace(3) global float undef, align 4
+; CHECK-NOT: @lds.5
+; CHECK-NOT: @lds.6
+; CHECK-NOT: @lds.7
+@lds.1 = addrspace(3) global i16 undef, align 2
+@lds.2 = addrspace(3) global i32 undef, align 4
+@lds.3 = addrspace(3) global i64 undef, align 8
+@lds.4 = addrspace(3) global float undef, align 4
+@lds.5 = addrspace(3) global [1 x float] undef, align 4
+@lds.6 = addrspace(3) global [2 x float] undef, align 8
+@lds.7 = addrspace(3) global [3 x float] undef, align 16
+
+; CHECK: @gptr.3 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* @lds.3 to i64*), align 8
+; CHECK: @gptr.4 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast (float addrspace(3)* @lds.4 to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.5 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast ([1 x float] addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 1) to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.6 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast ([2 x float] addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 2) to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.7 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* bitcast (%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds to i64 addrspace(3)*) to i64*), align 8
+; CHECK: @gptr.8 = addrspace(1) global i64** addrspacecast (i64* addrspace(1)* @gptr.7 to i64**), align 8
+@gptr.3 = addrspace(1) global i64* addrspacecast (i64 addrspace(3)* @lds.3 to i64*), align 8
+@gptr.4 = addrspace(1) global i64* addrspacecast (float addrspace(3)* @lds.4 to i64*), align 8
+@gptr.5 = addrspace(1) global i64* addrspacecast ([1 x float] addrspace(3)* @lds.5 to i64*), align 8
+@gptr.6 = addrspace(1) global i64* addrspacecast ([2 x float] addrspace(3)* @lds.6 to i64*), align 8
+@gptr.7 = addrspace(1) global i64* addrspacecast ([3 x float] addrspace(3)* @lds.7 to i64*), align 8
+@gptr.8 = addrspace(1) global i64** addrspacecast (i64* addrspace(1)* @gptr.7 to i64**), align 8
+
+; CHECK: @llvm.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i16 addrspace(3)* @lds.1 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+; CHECK: @llvm.amdgcn.module.lds = internal addrspace(3) global %llvm.amdgcn.module.lds.t undef, align 16
+; CHECK: @llvm.compiler.used = appending global [5 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @lds.2 to i8 addrspace(3)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.4 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(3)* bitcast (%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+@llvm.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i16 addrspace(3)* @lds.1 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
+@llvm.compiler.used = appending global [4 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @lds.2 to i8 addrspace(3)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.4 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i8 addrspace(1)*) to i8*), i8* addrspacecast (i8 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i8 addrspace(1)*) to i8*)], section "llvm.metadata"
+
+; CHECK-LABEL: @f1()
+; CHECK: %ld = load i64**, i64** addrspace(1)* @gptr.8, align 8
+; CHECK: ret void
+define void @f1() {
+ %ld = load i64**, i64** addrspace(1)* @gptr.8
+ ret void
+}
+
+; CHECK-LABEL: @f0()
+; CHECK: %ld = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i32 addrspace(1)*) to i32*) to i64), i64 ptrtoint (i32* addrspacecast (i32
+; CHECK: addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i32 addrspace(1)*) to i32*) to i64)) to i32*), align 4
+; CHECK: ret void
+define void @f0() {
+ %ld = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i32 addrspace(1)*) to i32*) to i64), i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.6 to i32 addrspace(1)*) to i32*) to i64)) to i32*), align 4
+ ret void
+}
+
+; CHECK-LABEL: @k0()
+; CHECK: call void @llvm.donothing() [ "ExplicitUse"(%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds) ]
+; CHECK: %ld = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i32 addrspace(1)*) to i32*) to i64), i64 ptrtoint (i32* addrspacecast (i32
+; CHECK: addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i32 addrspace(1)*) to i32*) to i64)) to i32*), align 4
+; CHECK: ret void
+define amdgpu_kernel void @k0() {
+ %ld = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i32 addrspace(1)*) to i32*) to i64), i64 ptrtoint (i32* addrspacecast (i32 addrspace(1)* bitcast (i64* addrspace(1)* @gptr.5 to i32 addrspace(1)*) to i32*) to i64)) to i32*), align 4
+ ret void
+}
+
+; CHECK-LABEL: @k1()
+; CHECK: call void @llvm.donothing() [ "ExplicitUse"(%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds) ]
+; CHECK: ret void
+define amdgpu_kernel void @k1() {
+ ret void
+}
--- /dev/null
+; RUN: opt -S -mtriple=amdgcn-- -amdgpu-lower-module-lds < %s | FileCheck %s
+; RUN: opt -S -mtriple=amdgcn-- -passes=amdgpu-lower-module-lds < %s | FileCheck %s
+
+; CHECK: %llvm.amdgcn.module.lds.t = type { double, float }
+
+; CHECK: @function_indirect = addrspace(1) global float* addrspacecast (float addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 1) to float*), align 8
+
+; CHECK: @kernel_indirect = addrspace(1) global double* addrspacecast (double addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 0) to double*), align 8
+
+; CHECK: @llvm.amdgcn.module.lds = internal addrspace(3) global %llvm.amdgcn.module.lds.t undef, align 8
+
+@function_target = addrspace(3) global float undef, align 4
+@function_indirect = addrspace(1) global float* addrspacecast (float addrspace(3)* @function_target to float*), align 8
+
+@kernel_target = addrspace(3) global double undef, align 8
+@kernel_indirect = addrspace(1) global double* addrspacecast (double addrspace(3)* @kernel_target to double*), align 8
+
+; CHECK-LABEL: @function(float %x)
+; CHECK: %0 = load float*, float* addrspace(1)* @function_indirect, align 8
+define void @function(float %x) local_unnamed_addr #5 {
+entry:
+ %0 = load float*, float* addrspace(1)* @function_indirect, align 8
+ store float %x, float* %0, align 4
+ ret void
+}
+
+; CHECK-LABEL: @kernel(double %x)
+; CHECK: call void @llvm.donothing() [ "ExplicitUse"(%llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds) ]
+; CHECK: %0 = load double*, double* addrspace(1)* @kernel_indirect, align 8
+define amdgpu_kernel void @kernel(double %x) local_unnamed_addr #5 {
+entry:
+ %0 = load double*, double* addrspace(1)* @kernel_indirect, align 8
+ store double %x, double* %0, align 8
+ ret void
+}
+
+
+
+
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -march=amdgcn -mcpu=tahiti -verify-machineinstrs | FileCheck -check-prefix=SI %s
-; RUN: llc < %s -march=amdgcn -mcpu=tonga -verify-machineinstrs | FileCheck -check-prefix=VI %s
-; RUN: llc < %s -march=amdgcn -mcpu=gfx90a -verify-machineinstrs | FileCheck -check-prefix=GFX90A %s
+; RUN: llc < %s -march=amdgcn -mcpu=tahiti -verify-machineinstrs | FileCheck -check-prefixes=SI,GCN %s
+; RUN: llc < %s -march=amdgcn -mcpu=tonga -verify-machineinstrs | FileCheck -check-prefixes=VI,GCN %s
+; GCN-LABEL: {{^}}select0:
+; i64 select should be split into two i32 selects, and we shouldn't need
+; to use a shfit to extract the hi dword of the input.
+; GCN-NOT: s_lshr_b64
+; GCN: v_cndmask
+; GCN: v_cndmask
define amdgpu_kernel void @select0(i64 addrspace(1)* %out, i32 %cond, i64 %in) {
+<<<<<<< HEAD
; SI-LABEL: select0:
; SI: ; %bb.0: ; %entry
; SI-NEXT: s_load_dword s6, s[0:1], 0xb
; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[0:1], s[0:1] op_sel:[0,1]
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
entry:
%0 = icmp ugt i32 %cond, 5
%1 = select i1 %0, i64 0, i64 %in
ret void
}
+; GCN-LABEL: {{^}}select_trunc_i64:
+; VI: s_cselect_b32
+; VI-NOT: s_cselect_b32
+; SI: v_cndmask_b32
+; SI-NOT: v_cndmask_b32
define amdgpu_kernel void @select_trunc_i64(i32 addrspace(1)* %out, i32 %cond, i64 %in) nounwind {
+<<<<<<< HEAD
; SI-LABEL: select_trunc_i64:
; SI: ; %bb.0:
; SI-NEXT: s_load_dword s4, s[0:1], 0xb
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%cmp = icmp ugt i32 %cond, 5
%sel = select i1 %cmp, i64 0, i64 %in
%trunc = trunc i64 %sel to i32
ret void
}
+; GCN-LABEL: {{^}}select_trunc_i64_2:
+; VI: s_cselect_b32
+; VI-NOT: s_cselect_b32
+; SI: v_cndmask_b32
+; SI-NOT: v_cndmask_b32
define amdgpu_kernel void @select_trunc_i64_2(i32 addrspace(1)* %out, i32 %cond, i64 %a, i64 %b) nounwind {
+<<<<<<< HEAD
; SI-LABEL: select_trunc_i64_2:
; SI: ; %bb.0:
; SI-NEXT: s_load_dword s8, s[0:1], 0xb
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%cmp = icmp ugt i32 %cond, 5
%sel = select i1 %cmp, i64 %a, i64 %b
%trunc = trunc i64 %sel to i32
ret void
}
+; GCN-LABEL: {{^}}v_select_trunc_i64_2:
+; VI: s_cselect_b32
+; VI-NOT: s_cselect_b32
+; SI: v_cndmask_b32
+; SI-NOT: v_cndmask_b32
define amdgpu_kernel void @v_select_trunc_i64_2(i32 addrspace(1)* %out, i32 %cond, i64 addrspace(1)* %aptr, i64 addrspace(1)* %bptr) nounwind {
+<<<<<<< HEAD
; SI-LABEL: v_select_trunc_i64_2:
; SI: ; %bb.0:
; SI-NEXT: s_load_dwordx4 s[4:7], s[0:1], 0xd
; GFX90A-NEXT: v_mov_b32_e32 v1, s0
; GFX90A-NEXT: global_store_dword v0, v1, s[2:3]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%cmp = icmp ugt i32 %cond, 5
%a = load i64, i64 addrspace(1)* %aptr, align 8
%b = load i64, i64 addrspace(1)* %bptr, align 8
ret void
}
+; GCN-LABEL: {{^}}v_select_i64_split_imm:
+; GCN-DAG: v_cndmask_b32_e32 {{v[0-9]+}}, 0, {{v[0-9]+}}
+; GCN-DAG: v_cndmask_b32_e32 {{v[0-9]+}}, 63, {{v[0-9]+}}
+; GCN: s_endpgm
define amdgpu_kernel void @v_select_i64_split_imm(i64 addrspace(1)* %out, i32 %cond, i64 addrspace(1)* %aptr, i64 addrspace(1)* %bptr) nounwind {
+<<<<<<< HEAD
; SI-LABEL: v_select_i64_split_imm:
; SI: ; %bb.0:
; SI-NEXT: s_load_dwordx2 s[2:3], s[0:1], 0xd
; GFX90A-NEXT: v_pk_mov_b32 v[0:1], s[0:1], s[0:1] op_sel:[0,1]
; GFX90A-NEXT: global_store_dwordx2 v2, v[0:1], s[4:5]
; GFX90A-NEXT: s_endpgm
+=======
+>>>>>>> parent of 640beb38e771... [amdgpu] Enable selection of `s_cselect_b64`.
%cmp = icmp ugt i32 %cond, 5
%a = load i64, i64 addrspace(1)* %aptr, align 8
%b = load i64, i64 addrspace(1)* %bptr, align 8
; RUN: llc -verify-machineinstrs -march=r600 -mcpu=redwood < %s | FileCheck -check-prefix=EG -check-prefix=FUNC %s
-; RUN: llc -verify-machineinstrs -march=amdgcn -mcpu=tahiti < %s | FileCheck -check-prefix=SI -check-prefix=FUNC %s
-; RUN: llc -verify-machineinstrs -march=amdgcn -mcpu=tonga < %s | FileCheck -check-prefix=VI -check-prefix=FUNC %s
+; RUN: llc -verify-machineinstrs -march=amdgcn -mcpu=tahiti < %s | FileCheck -check-prefixes=GCN,SI -check-prefix=FUNC %s
+; RUN: llc -verify-machineinstrs -march=amdgcn -mcpu=tonga < %s | FileCheck -check-prefixes=GCN,VI -check-prefix=FUNC %s
; FUNC-LABEL: {{^}}selectcc_i64:
; EG: XOR_INT
; EG: CNDE_INT
; EG: CNDE_INT
; SI: v_cmp_eq_u64
-; SI: v_cndmask
-; SI: v_cndmask
; VI: s_cmp_eq_u64
-; VI: s_cselect_b64
+; GCN: v_cndmask
+; GCN: v_cndmask
define amdgpu_kernel void @selectcc_i64(i64 addrspace(1) * %out, i64 %lhs, i64 %rhs, i64 %true, i64 %false) {
entry:
%0 = icmp eq i64 %lhs, %rhs
; GCN-LABEL: {{^}}sint_to_fp_i1_f64:
; VI-DAG: s_cmp_eq_u32
-; VI-DAG: s_cselect_b64 s{{\[}}[[S_LO:[0-9]+]]:[[S_HI:[0-9]+]]{{\]}}, -1.0, 0
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[S_LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[S_HI]]
-; VI: flat_store_dwordx2 v{{\[[0-9]+:[0-9]+\]}}, v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; VI-DAG: s_cselect_b32 s[[SSEL:[0-9]+]], 0xbff00000, 0
+; VI-DAG: v_mov_b32_e32 v[[ZERO:[0-9]+]], 0{{$}}
+; VI-DAG: v_mov_b32_e32 v[[SEL:[0-9]+]], s[[SSEL]]
+; VI: flat_store_dwordx2 v{{\[[0-9]+:[0-9]+\]}}, v{{\[}}[[ZERO]]:[[SEL]]{{\]}}
; VI: s_endpgm
; SI-DAG: s_cmp_eq_u32
; GCN-LABEL: {{^}}uint_to_fp_i1_to_f64:
; VI-DAG: s_cmp_eq_u32
-; VI-DAG: s_cselect_b64 s{{\[}}[[S_LO:[0-9]+]]:[[S_HI:[0-9]+]]{{\]}}, 1.0, 0
-; VI-DAG: v_mov_b32_e32 v[[V_LO:[0-9]+]], s[[S_LO]]
-; VI-DAG: v_mov_b32_e32 v[[V_HI:[0-9]+]], s[[S_HI]]
-; VI: flat_store_dwordx2 v{{\[[0-9]+:[0-9]+\]}}, v{{\[}}[[V_LO]]:[[V_HI]]{{\]}}
+; VI-DAG: s_cselect_b32 s[[SSEL:[0-9]+]], 0x3ff00000, 0
+; VI-DAG: v_mov_b32_e32 v[[SEL:[0-9]+]], s[[SSEL]]
; SI-DAG: s_cmp_eq_u32
; SI-DAG: s_cselect_b64 vcc, -1, 0
; SI-DAG: v_cndmask_b32_e32 v[[SEL:[0-9]+]], 0, {{v[0-9]+}}, vcc
-; SI-DAG: v_mov_b32_e32 v[[ZERO:[0-9]+]], 0{{$}}
-; SI: flat_store_dwordx2 v{{\[[0-9]+:[0-9]+\]}}, v{{\[}}[[ZERO]]:[[SEL]]{{\]}}
+; GCN-DAG: v_mov_b32_e32 v[[ZERO:[0-9]+]], 0{{$}}
+; GCN: flat_store_dwordx2 v{{\[[0-9]+:[0-9]+\]}}, v{{\[}}[[ZERO]]:[[SEL]]{{\]}}
; GCN: s_endpgm
define amdgpu_kernel void @uint_to_fp_i1_to_f64(double addrspace(1)* %out, i32 %in) {
%cmp = icmp eq i32 %in, 0
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -o - %s -mtriple=x86_64-- | FileCheck %s
+; Example of a decref operation with "immortal" objects.
+; void decref(long* refcount) {
+; long count = *refcount;
+; if (count == 1) { free_object() }
+; else if (count > 1) { *refcount = count - 1; }
+; else { /* immortal */ }
+; }
+; Resulting assembly should share flags from single CMP instruction for both
+; conditions!
+define void @decref(i32* %p) {
+; CHECK-LABEL: decref:
+; CHECK: # %bb.0:
+; CHECK-NEXT: pushq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: movl (%rdi), %eax
+; CHECK-NEXT: cmpl $1, %eax
+; CHECK-NEXT: jne .LBB0_2
+; CHECK-NEXT: # %bb.1: # %bb_free
+; CHECK-NEXT: callq free_object@PLT
+; CHECK-NEXT: .LBB0_4: # %end
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: retq
+; CHECK-NEXT: .LBB0_2: # %bb2
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: jle .LBB0_4
+; CHECK-NEXT: # %bb.3: # %bb_dec
+; CHECK-NEXT: decl %eax
+; CHECK-NEXT: movl %eax, (%rdi)
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: retq
+ %count = load i32, i32* %p, align 4
+ %cmp0 = icmp eq i32 %count, 1
+ br i1 %cmp0, label %bb_free, label %bb2
+
+bb2:
+ %cmp1 = icmp sgt i32 %count, 1
+ br i1 %cmp1, label %bb_dec, label %end
+
+bb_dec:
+ %dec = add nsw i32 %count, -1
+ store i32 %dec, i32* %p, align 4
+ br label %end
+
+bb_free:
+ call void @free_object()
+ br label %end
+
+end:
+ ret void
+}
+
+declare void @free_object()
--- /dev/null
+# Supplies a weak def, WeakDef, and a pointer holding its address,
+# WeakDefAddrInExtraFile.
+
+ .section __TEXT,__text,regular,pure_instructions
+ .build_version macos, 10, 14 sdk_version 10, 14
+ .section __DATA,__data
+ .globl WeakDef
+ .weak_definition WeakDef
+ .p2align 2
+WeakDef:
+ .long 2
+
+ .globl WeakDefAddrInExtraFile
+ .p2align 3
+WeakDefAddrInExtraFile:
+ .quad WeakDef
+
+
+.subsections_via_symbols
--- /dev/null
+# REQUIRES: asserts
+# RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -filetype=obj -o %t %s
+# RUN: llvm-jitlink -debug-only=jitlink -noexec %t 2>&1 | FileCheck %s
+#
+# Check that debug sections are not emitted, and consequently that we don't
+# error out due to buggy past-the-end anonymous relocations in __debug_ranges.
+#
+# CHECK: __debug_ranges is a debug section: No graph section will be created.
+ .section __TEXT,__text,regular,pure_instructions
+ .macosx_version_min 10, 15
+ .globl _main
+ .p2align 4, 0x90
+_main:
+ retq
+Lpast_the_end:
+
+ .section __DWARF,__debug_ranges
+ .p2align 4
+ .quad Lpast_the_end
+
+.subsections_via_symbols
--- /dev/null
+; RUN: opt -passes='sample-profile,function(loop-mssa(licm))' -aa-pipeline=basic-aa -S -sample-profile-file='%S/Inputs/no-hoist-prof.prof' < %s | FileCheck %s --check-prefix=CHECK-BFI-LICM
+; RUN: opt -passes=licm -S < %s | FileCheck %s --check-prefix=CHECK-LICM
+
+; Original source code:
+;
+; int bar(int);
+; int foo(int iter, int explode) {
+; int base = bar(explode);
+; for (int i = 0; i != iter; ++i)
+; if (i == explode)
+; iter = (base * base) + bar(iter);
+; return iter;
+; }
+
+; We need debug information in this .ll in order to leverage the pgo file, so:
+; .ll generated by running `clang++ -O3 -g -S -emit-llvm`, then:
+; - move hoisted mul back into cold section
+; - give labels names
+; - reindex variables
+; - remove metadata calls, attributes, module header
+; - remove unnecessary metadata
+
+; CHECK-LICM: .l.check.preheader:{{.*}}
+; CHECK-LICM-NEXT: {{.*}} = mul {{.*}}
+; CHECK-LICM-NEXT: br{{.*}}
+
+; CHECK-BFI-LICM: .l.cold:{{.*}}
+; CHECK-BFI-LICM-NEXT: {{.*}} = mul {{.*}}
+
+define dso_local i32 @_Z3fooii(i32, i32) local_unnamed_addr #0 !dbg !7 {
+ %3 = tail call i32 @_Z3bari(i32 %1), !dbg !19
+ %4 = icmp eq i32 %0, 0, !dbg !22
+ br i1 %4, label %.l.ret, label %.l.check.preheader, !dbg !24
+
+.l.check.preheader:
+ br label %.l.check, !dbg !24
+
+.l.ret:
+ %5 = phi i32 [ 0, %2 ], [ %12, %.l.iterate ]
+ ret i32 %5, !dbg !25
+
+.l.check:
+ %6 = phi i32 [ 0, %.l.check.preheader ], [ %13, %.l.iterate ]
+ %7 = phi i32 [ %0, %.l.check.preheader ], [ %12, %.l.iterate ]
+ %8 = icmp eq i32 %6, %1, !dbg !26
+ br i1 %8, label %.l.cold, label %.l.iterate, !dbg !28
+
+.l.cold:
+ %9 = mul nsw i32 %3, %3
+ %10 = tail call i32 @_Z3bari(i32 %7), !dbg !29
+ %11 = add nsw i32 %10, %9, !dbg !30
+ br label %.l.iterate, !dbg !31
+
+.l.iterate:
+ %12 = phi i32 [ %11, %.l.cold ], [ %7, %.l.check ]
+ %13 = add nuw nsw i32 %6, 1, !dbg !32
+ %14 = icmp eq i32 %13, %12, !dbg !22
+ br i1 %14, label %.l.ret, label %.l.check, !dbg !24, !llvm.loop !33
+}
+
+attributes #0 = { "use-sample-profile" }
+
+declare dso_local i32 @_Z3bari(i32) local_unnamed_addr #1
+
+!llvm.module.flags = !{!4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.20181009 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, nameTableKind: None)
+!1 = !DIFile(filename: "foo.cpp", directory: "/tmp/gather_pgo")
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10, !10, !10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!16 = distinct !DILexicalBlock(scope: !7, file: !1, line: 4, column: 3)
+!19 = !DILocation(line: 3, column: 14, scope: !7)
+!22 = !DILocation(line: 4, column: 21, scope: !23)
+!23 = distinct !DILexicalBlock(scope: !16, file: !1, line: 4, column: 3)
+!24 = !DILocation(line: 4, column: 3, scope: !16)
+!25 = !DILocation(line: 7, column: 3, scope: !7)
+!26 = !DILocation(line: 5, column: 11, scope: !27)
+!27 = distinct !DILexicalBlock(scope: !23, file: !1, line: 5, column: 9)
+!28 = !DILocation(line: 5, column: 9, scope: !23)
+!29 = !DILocation(line: 6, column: 30, scope: !27)
+!30 = !DILocation(line: 6, column: 28, scope: !27)
+!31 = !DILocation(line: 6, column: 7, scope: !27)
+!32 = !DILocation(line: 4, column: 30, scope: !23)
+!33 = distinct !{!33, !24, !34}
+!34 = !DILocation(line: 6, column: 38, scope: !16)
--- /dev/null
+; RUN: opt -S -licm -licm-coldness-threshold=0 < %s | FileCheck %s --check-prefix=CHECK-LICM
+; RUN: opt -S -licm < %s | opt -S -loop-sink | FileCheck %s --check-prefix=CHECK-SINK
+; RUN: opt -S < %s -passes='require<opt-remark-emit>,loop-mssa(licm),loop-sink' \
+; RUN: | FileCheck %s --check-prefix=CHECK-SINK
+; RUN: opt -S -licm -licm-coldness-threshold=0 -verify-memoryssa < %s | FileCheck %s --check-prefix=CHECK-LICM
+; RUN: opt -S -licm -verify-memoryssa < %s | FileCheck %s --check-prefix=CHECK-BFI-LICM
+
+; Original source code:
+; int g;
+; int foo(int p, int x) {
+; for (int i = 0; i != x; i++)
+; if (__builtin_expect(i == p, 0)) {
+; x += g; x *= g;
+; }
+; return x;
+; }
+;
+; Load of global value g should not be hoisted to preheader.
+
+@g = global i32 0, align 4
+
+define i32 @foo(i32, i32) #0 !prof !2 {
+ %3 = icmp eq i32 %1, 0
+ br i1 %3, label %._crit_edge, label %.lr.ph.preheader
+
+.lr.ph.preheader:
+ br label %.lr.ph
+
+; CHECK-LICM: .lr.ph.preheader:
+; CHECK-LICM: load i32, i32* @g
+; CHECK-LICM: br label %.lr.ph
+
+; CHECK-BFI-LICM: .lr.ph.preheader:
+; CHECK-BFI-LICM-NOT: load i32, i32* @g
+; CHECK-BFI-LICM: br label %.lr.ph
+
+.lr.ph:
+ %.03 = phi i32 [ %8, %.combine ], [ 0, %.lr.ph.preheader ]
+ %.012 = phi i32 [ %.1, %.combine ], [ %1, %.lr.ph.preheader ]
+ %4 = icmp eq i32 %.03, %0
+ br i1 %4, label %.then, label %.combine, !prof !1
+
+.then:
+ %5 = load i32, i32* @g, align 4
+ %6 = add nsw i32 %5, %.012
+ %7 = mul nsw i32 %6, %5
+ br label %.combine
+
+; CHECK-SINK: .then:
+; CHECK-SINK: load i32, i32* @g
+; CHECK-SINK: br label %.combine
+
+.combine:
+ %.1 = phi i32 [ %7, %.then ], [ %.012, %.lr.ph ]
+ %8 = add nuw nsw i32 %.03, 1
+ %9 = icmp eq i32 %8, %.1
+ br i1 %9, label %._crit_edge.loopexit, label %.lr.ph
+
+._crit_edge.loopexit:
+ %.1.lcssa = phi i32 [ %.1, %.combine ]
+ br label %._crit_edge
+
+._crit_edge:
+ %.01.lcssa = phi i32 [ 0, %2 ], [ %.1.lcssa, %._crit_edge.loopexit ]
+ ret i32 %.01.lcssa
+}
+
+!1 = !{!"branch_weights", i32 1, i32 2000}
+!2 = !{!"function_entry_count", i64 1}
--- /dev/null
+; RUN: llvm-as -disable-output <%s 2>&1 | FileCheck %s
+; CHECK: enum type is not a scope; check enum type ODR violation
+; CHECK: warning: ignoring invalid debug info
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!llvm.dbg.cu = !{!1}
+!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !13, enums: !3)
+!2 = !DIFile(filename: "file.c", directory: "dir")
+!3 = !{!4}
+!4 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, name: "Stage", file: !2, line: 3, baseType: !10, size: 32, elements: !11, identifier: "_ZTS5Stage")
+!6 = !DIDerivedType(tag: DW_TAG_member, name: "Var", scope: !4, file: !2, line: 5, baseType: !10)
+!10 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!11 = !{!12}
+!12 = !DIEnumerator(name: "A1", value: 0, isUnsigned: true)
+!13 = !{!6}
--- /dev/null
+if not 'X86' in config.root.targets:
+ config.unsupported = True