text: //===----------------------------------------------------------------------===//
  //
  // 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 <cstdint.hxx>
  #include <functional.hxx>
  #include <memory.hxx>
  #include <string.hxx>
  
  #include "CartesianBenchmarks.h"
  #include "benchmark/benchmark.h"
  #include "test_macros.h"
  
  namespace {
  
  enum class FunctionType {
    Null,
    FunctionPointer,
    MemberFunctionPointer,
    MemberPointer,
    SmallTrivialFunctor,
    SmallNonTrivialFunctor,
    LargeTrivialFunctor,
    LargeNonTrivialFunctor
  };
  
  struct AllFunctionTypes : EnumValuesAsTuple<AllFunctionTypes, FunctionType, 8> {
    static constexpr const char* Names[] = {"Null",
                                            "FuncPtr",
                                            "MemFuncPtr",
                                            "MemPtr",
                                            "SmallTrivialFunctor",
                                            "SmallNonTrivialFunctor",
                                            "LargeTrivialFunctor",
                                            "LargeNonTrivialFunctor"};
  };
  
  enum class Opacity { kOpaque, kTransparent };
  
  struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
    static constexpr const char* Names[] = {"Opaque", "Transparent"};
  };
  
  struct S {
    int function() const { return 0; }
    int field = 0;
  };
  
  int FunctionWithS(const S*) { return 0; }
  
  struct SmallTrivialFunctor {
    int operator()(const S*) const { return 0; }
  };
  struct SmallNonTrivialFunctor {
    SmallNonTrivialFunctor() {}
    SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
    ~SmallNonTrivialFunctor() {}
    int operator()(const S*) const { return 0; }
  };
  struct LargeTrivialFunctor {
    LargeTrivialFunctor() {
        // Do not spend time initializing the padding.
    }
    int padding[16];
    int operator()(const S*) const { return 0; }
  };
  struct LargeNonTrivialFunctor {
    int padding[16];
    LargeNonTrivialFunctor() {
        // Do not spend time initializing the padding.
    }
    LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {}
    ~LargeNonTrivialFunctor() {}
    int operator()(const S*) const { return 0; }
  };
  
  using Function = std::function<int(const S*)>;
  
  TEST_ALWAYS_INLINE
  inline Function MakeFunction(FunctionType type, bool opaque = false) {
    switch (type) {
      case FunctionType::Null:
        return nullptr;
      case FunctionType::FunctionPointer:
        return maybeOpaque(FunctionWithS, opaque);
      case FunctionType::MemberFunctionPointer:
        return maybeOpaque(&S::function, opaque);
      case FunctionType::MemberPointer:
        return maybeOpaque(&S::field, opaque);
      case FunctionType::SmallTrivialFunctor:
        return maybeOpaque(SmallTrivialFunctor{}, opaque);
      case FunctionType::SmallNonTrivialFunctor:
        return maybeOpaque(SmallNonTrivialFunctor{}, opaque);
      case FunctionType::LargeTrivialFunctor:
        return maybeOpaque(LargeTrivialFunctor{}, opaque);
      case FunctionType::LargeNonTrivialFunctor:
        return maybeOpaque(LargeNonTrivialFunctor{}, opaque);
    }
  }
  
  template <class Opacity, class FunctionType>
  struct ConstructAndDestroy {
    static void run(benchmark::State& state) {
      for (auto _ : state) {
        if (Opacity() == ::Opacity::kOpaque) {
          benchmark::DoNotOptimize(MakeFunction(FunctionType(), true));
        } else {
          MakeFunction(FunctionType());
        }
      }
    }
  
    static std::string name() {
      return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name();
    }
  };
  
  template <class FunctionType>
  struct Copy {
    static void run(benchmark::State& state) {
      auto value = MakeFunction(FunctionType());
      for (auto _ : state) {
        benchmark::DoNotOptimize(value);
        auto copy = value;  // NOLINT
        benchmark::DoNotOptimize(copy);
      }
    }
  
    static std::string name() { return "BM_Copy" + FunctionType::name(); }
  };
  
  template <class FunctionType>
  struct Move {
    static void run(benchmark::State& state) {
      Function values[2] = {MakeFunction(FunctionType())};
      int i = 0;
      for (auto _ : state) {
        benchmark::DoNotOptimize(values);
        benchmark::DoNotOptimize(values[i ^ 1] = std::move(values[i]));
        i ^= 1;
      }
    }
  
    static std::string name() {
      return "BM_Move" + FunctionType::name();
    }
  };
  
  template <class Function1, class Function2>
  struct Swap {
    static void run(benchmark::State& state) {
      Function values[2] = {MakeFunction(Function1()), MakeFunction(Function2())};
      for (auto _ : state) {
        benchmark::DoNotOptimize(values);
        values[0].swap(values[1]);
      }
    }
  
    static bool skip() { return Function1() > Function2(); }
  
    static std::string name() {
      return "BM_Swap" + Function1::name() + Function2::name();
    }
  };
  
  template <class FunctionType>
  struct OperatorBool {
    static void run(benchmark::State& state) {
      auto f = MakeFunction(FunctionType());
      for (auto _ : state) {
        benchmark::DoNotOptimize(f);
        benchmark::DoNotOptimize(static_cast<bool>(f));
      }
    }
  
    static std::string name() { return "BM_OperatorBool" + FunctionType::name(); }
  };
  
  template <class FunctionType>
  struct Invoke {
    static void run(benchmark::State& state) {
      S s;
      const auto value = MakeFunction(FunctionType());
      for (auto _ : state) {
        benchmark::DoNotOptimize(value);
        benchmark::DoNotOptimize(value(&s));
      }
    }
  
    static bool skip() { return FunctionType() == ::FunctionType::Null; }
  
    static std::string name() { return "BM_Invoke" + FunctionType::name(); }
  };
  
  template <class FunctionType>
  struct InvokeInlined {
    static void run(benchmark::State& state) {
      S s;
      for (auto _ : state) {
        MakeFunction(FunctionType())(&s);
      }
    }
  
    static bool skip() { return FunctionType() == ::FunctionType::Null; }
  
    static std::string name() {
      return "BM_InvokeInlined" + FunctionType::name();
    }
  };
  
  }  // namespace
  
  int main(int argc, char** argv) {
    benchmark::Initialize(&argc, argv);
    if (benchmark::ReportUnrecognizedArguments(argc, argv))
      return 1;
  
    makeCartesianProductBenchmark<ConstructAndDestroy, AllOpacity,
                                  AllFunctionTypes>();
    makeCartesianProductBenchmark<Copy, AllFunctionTypes>();
    makeCartesianProductBenchmark<Move, AllFunctionTypes>();
    makeCartesianProductBenchmark<Swap, AllFunctionTypes, AllFunctionTypes>();
    makeCartesianProductBenchmark<OperatorBool, AllFunctionTypes>();
    makeCartesianProductBenchmark<Invoke, AllFunctionTypes>();
    makeCartesianProductBenchmark<InvokeInlined, AllFunctionTypes>();
    benchmark::RunSpecifiedBenchmarks();
  }
  