Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/Progmasoft/Catch3/Benchmark/Optimizer.hpp
blob: 1b1d309163b766e286451dd01d8fbf8bba8dd93a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

//              Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
//   (See accompanying file LICENSE.txt or copy at
//        https://www.boost.org/LICENSE_1_0.txt)

// SPDX-License-Identifier: BSL-1.0
// Adapted from donated nonius code.

#pragma once

#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
#   include <atomic> // atomic_thread_fence
#endif

#include <Progmasoft/Catch3/Internal/MoveAndForward.hpp>

#include <type_traits>

namespace Catch {
    namespace Benchmark {
#if defined(__GNUC__) || defined(__clang__)
        template <typename T>
        inline void keep_memory(T* p) {
            asm volatile("" : : "g"(p) : "memory");
        }
        inline void keep_memory() {
            asm volatile("" : : : "memory");
        }

        namespace Detail {
            inline void optimizer_barrier() { keep_memory(); }
        } // namespace Detail
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)

#if defined(_MSC_VER)
#pragma optimize("", off)
#elif defined(__IAR_SYSTEMS_ICC__)
// For IAR the pragma only affects the following function
#pragma optimize=disable
#endif
        template <typename T>
        inline void keep_memory(T* p) {
            // thanks @milleniumbug
            *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
        }
        // TODO equivalent keep_memory()
#if defined(_MSC_VER)
#pragma optimize("", on)
#endif

        namespace Detail {
            inline void optimizer_barrier() {
                std::atomic_thread_fence(std::memory_order_seq_cst);
            }
        } // namespace Detail

#endif

        template <typename T>
        inline void deoptimize_value(T&& x) {
            keep_memory(&x);
        }

        template <typename Fn, typename... Args>
        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
            deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
        }

        template <typename Fn, typename... Args>
        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
            CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
            // In the non-void case, we pass the result through `deoptimize_value`
            // to force the compiler to keep it. We have no return value here,
            // but add an optimizer barrier (ideally a memory clobber) to force
            // the _side effects_ of the loop be visible (e.g. writes to globals).
            // Note that writes to benchmark-locals can be optimized away, as
            // we would expect in normal code.
            Detail::optimizer_barrier();
        }
    } // namespace Benchmark
} // namespace Catch