1 //===-- include/flang/Common/visit.h ----------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // common::visit() is a drop-in replacement for std::visit() that reduces both
10 // compiler build time and compiler execution time modestly, and reduces
11 // compiler build memory requirements significantly (overall & maximum).
12 // It does not require redefinition of std::variant<>.
13 //
14 // The C++ standard mandates that std::visit be O(1), but most variants are
15 // small and O(logN) is faster in practice to compile and execute, avoiding
16 // the need to build a dispatch table.
17 //
18 // Define FLANG_USE_STD_VISIT to avoid this code and make common::visit() an
19 // alias for ::std::visit().
20 //
21 //
22 // With GCC 9.3.0 on a Haswell x86 Ubuntu system, doing out-of-tree builds:
23 // Before:
24 // build:
25 // 6948.53user 212.48system 27:32.92elapsed 433%CPU
26 // (0avgtext+0avgdata 6429568maxresident)k
27 // 36181912inputs+8943720outputs (3613684major+97908699minor)pagefaults 0swaps
28 // execution of tests:
29 // 205.99user 26.05system 1:08.87elapsed 336%CPU
30 // (0avgtext+0avgdata 2671452maxresident)k
31 // 244432inputs+355464outputs (422major+8746468minor)pagefaults 0swaps
32 // After:
33 // build:
34 // 6651.91user 182.57system 25:15.73elapsed 450%CPU
35 // (0avgtext+0avgdata 6209296maxresident)k
36 // 17413480inputs+6376360outputs (1567210major+93068230minor)pagefaults 0swaps
37 // execution of tests:
38 // 201.42user 25.91system 1:04.68elapsed 351%CPU
39 // (0avgtext+0avgdata 2661424maxresident)k
40 // 238840inputs+295912outputs (428major+8489300minor)pagefaults 0swaps
41
42 #ifndef FORTRAN_COMMON_VISIT_H_
43 #define FORTRAN_COMMON_VISIT_H_
44
45 #include <type_traits>
46 #include <variant>
47
48 namespace Fortran::common {
49 namespace log2visit {
50
51 template <std::size_t LOW, std::size_t HIGH, typename RESULT, typename VISITOR,
52 typename... VARIANT>
Log2VisitHelper(VISITOR && visitor,std::size_t which,VARIANT &&...u)53 inline RESULT Log2VisitHelper(
54 VISITOR &&visitor, std::size_t which, VARIANT &&...u) {
55 if constexpr (LOW == HIGH) {
56 return visitor(std::get<LOW>(std::forward<VARIANT>(u))...);
57 } else {
58 static constexpr std::size_t mid{(HIGH + LOW) / 2};
59 if (which <= mid) {
60 return Log2VisitHelper<LOW, mid, RESULT>(
61 std::forward<VISITOR>(visitor), which, std::forward<VARIANT>(u)...);
62 } else {
63 return Log2VisitHelper<(mid + 1), HIGH, RESULT>(
64 std::forward<VISITOR>(visitor), which, std::forward<VARIANT>(u)...);
65 }
66 }
67 }
68
69 template <typename VISITOR, typename... VARIANT>
70 inline auto visit(VISITOR &&visitor, VARIANT &&...u)
71 -> decltype(visitor(std::get<0>(std::forward<VARIANT>(u))...)) {
72 using Result = decltype(visitor(std::get<0>(std::forward<VARIANT>(u))...));
73 if constexpr (sizeof...(u) == 1) {
74 static constexpr std::size_t high{
75 (std::variant_size_v<std::decay_t<decltype(u)>> * ...) - 1};
76 return Log2VisitHelper<0, high, Result>(std::forward<VISITOR>(visitor),
77 u.index()..., std::forward<VARIANT>(u)...);
78 } else {
79 // TODO: figure out how to do multiple variant arguments
80 return ::std::visit(
81 std::forward<VISITOR>(visitor), std::forward<VARIANT>(u)...);
82 }
83 }
84
85 } // namespace log2visit
86
87 // Some versions of clang have bugs that cause compilation to hang
88 // on these templates. MSVC and older GCC versions may work but are
89 // not well tested. So enable only for GCC 9 and better.
90 #if __GNUC__ < 9
91 #define FLANG_USE_STD_VISIT
92 #endif
93
94 #ifdef FLANG_USE_STD_VISIT
95 using ::std::visit;
96 #else
97 using Fortran::common::log2visit::visit;
98 #endif
99
100 } // namespace Fortran::common
101 #endif // FORTRAN_COMMON_VISIT_H_
102