1 : // Copyright 2016 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Implementation details for play_util.h
16 :
17 : #ifndef SYZYGY_BARD_EVENTS_PLAY_UTIL_IMPL_H_
18 : #define SYZYGY_BARD_EVENTS_PLAY_UTIL_IMPL_H_
19 :
20 : #include <cinttypes>
21 : #include <utility>
22 :
23 : #include "base/logging.h"
24 : #include "syzygy/trace/common/clock.h"
25 :
26 : namespace bard {
27 : namespace detail {
28 :
29 : // This warnings relates to optimizations and manually injected assembly code.
30 : #pragma warning(disable: 4740)
31 :
32 : // Turn off all optimizations. These functions need to be distinct so they
33 : // each generate a unique stack frame.
34 : #pragma optimize("", off)
35 :
36 : // Helper for extracting the return type of a function.
37 : // @tparam FunctionType The type of the function to be called.
38 : // @tparam ParamTypes The types of the function parameters.
39 : template<typename FunctionType, typename ...ParamTypes>
40 : struct GetReturnType {
41 : using type =
42 : decltype(std::declval<FunctionType>()(std::declval<ParamTypes>()...));
43 : };
44 :
45 : // A testing seam that allows having the InvokeFunctionWithStackIdHelper
46 : // report its function extents.
47 : enum : uint32_t { kGetFunctionExtentsDepth = 0xFFFFFFFF };
48 : extern const void* kInvokeFunctionBegin;
49 : extern const void* kInvokeFunctionEnd;
50 :
51 : // Workhorse for InvokeFunctionWithStackId.
52 : template<uint32_t kInvokeValue,
53 : typename FunctionType,
54 : typename ReturnType,
55 : typename ...ParamTypes>
56 : struct InvokeFunctionWithStackIdHelper {
57 : template<uint32_t kChildInvokerValue>
58 : using Invoke = InvokeFunctionWithStackIdHelper<kChildInvokerValue,
59 : FunctionType, ReturnType, ParamTypes...>;
60 :
61 : static __declspec(noinline) ReturnType Do(uint32_t depth, uint32_t stack_id,
62 i : FunctionType& function, ParamTypes... params) {
63 : function_start:
64 :
65 : // Special case for testing. This gets the extents of this function as
66 : // actually laid out in memory.
67 i : if (depth == kGetFunctionExtentsDepth) {
68 : __asm {
69 i : push eax
70 i : mov eax, function_start
71 i : mov kInvokeFunctionBegin, eax
72 i : mov eax, function_end
73 i : mov kInvokeFunctionEnd, eax
74 i : pop eax
75 : }
76 i : return function(params...);
77 : }
78 :
79 : // Outside of testing the depth should never be more than 8, as there are
80 : // only 8 nibbles in a 32-bit int.
81 i : DCHECK_GE(8u, depth);
82 :
83 : // Handle the base case.
84 i : if (depth == 0) {
85 i : DCHECK_EQ(0u, stack_id);
86 i : return function(params...);
87 : }
88 :
89 : // Get the lowest nibble, shift it out, and decrement the depth.
90 i : uint32_t invoke_id = stack_id & 0xF;
91 i : stack_id >>= 4;
92 i : --depth;
93 :
94 : // Dispatch to the appropriate child invoker, based on the bottom nibble
95 : // of the stack ID.
96 i : switch (invoke_id) {
97 i : case 0x0: return Invoke<0x0>().Do(depth, stack_id, function, params...);
98 i : case 0x1: return Invoke<0x1>().Do(depth, stack_id, function, params...);
99 i : case 0x2: return Invoke<0x2>().Do(depth, stack_id, function, params...);
100 i : case 0x3: return Invoke<0x3>().Do(depth, stack_id, function, params...);
101 i : case 0x4: return Invoke<0x4>().Do(depth, stack_id, function, params...);
102 i : case 0x5: return Invoke<0x5>().Do(depth, stack_id, function, params...);
103 i : case 0x6: return Invoke<0x6>().Do(depth, stack_id, function, params...);
104 i : case 0x7: return Invoke<0x7>().Do(depth, stack_id, function, params...);
105 i : case 0x8: return Invoke<0x8>().Do(depth, stack_id, function, params...);
106 i : case 0x9: return Invoke<0x9>().Do(depth, stack_id, function, params...);
107 i : case 0xA: return Invoke<0xA>().Do(depth, stack_id, function, params...);
108 i : case 0xB: return Invoke<0xB>().Do(depth, stack_id, function, params...);
109 i : case 0xC: return Invoke<0xC>().Do(depth, stack_id, function, params...);
110 i : case 0xD: return Invoke<0xD>().Do(depth, stack_id, function, params...);
111 i : case 0xE: return Invoke<0xE>().Do(depth, stack_id, function, params...);
112 i : case 0xF: return Invoke<0xF>().Do(depth, stack_id, function, params...);
113 : default: break;
114 : }
115 :
116 : function_end:
117 i : NOTREACHED();
118 i : return function(params...);
119 i : }
120 : };
121 :
122 : // Implementation of InvokeOnBackdrop.
123 : template<typename BackdropType, typename ReturnType, typename ...ParamTypes>
124 : struct InvokeOnBackdropHelper {
125 : static ReturnType DoImpl(uint64_t* timing,
126 : BackdropType* backdrop,
127 : ReturnType (BackdropType::*function)(ParamTypes...),
128 E : ParamTypes... params) {
129 E : uint64_t t0 = ::trace::common::GetTsc();
130 E : ReturnType ret = (backdrop->*function)(params...);
131 E : uint64_t t1 = ::trace::common::GetTsc();
132 E : *timing = t1 - t0;
133 E : return ret;
134 E : }
135 :
136 : static ReturnType Do(uint32_t stack_id,
137 : uint64_t* timing,
138 : BackdropType* backdrop,
139 : ReturnType (BackdropType::*function)(ParamTypes...),
140 E : ParamTypes... params) {
141 E : return InvokeFunctionWithStackId(
142 : stack_id, DoImpl, timing, backdrop, function, params...);
143 E : }
144 : };
145 :
146 : #pragma optimize("", on)
147 :
148 : } // namespace detail
149 : } // namespace bard
150 :
151 : #endif // SYZYGY_BARD_EVENTS_PLAY_UTIL_IMPL_H_
|