1 /*
2  *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #ifndef TEST_REGISTER_STATE_CHECK_H_
12 #define TEST_REGISTER_STATE_CHECK_H_
13 
14 #include "third_party/googletest/src/include/gtest/gtest.h"
15 #include "./vpx_config.h"
16 #include "vpx/vpx_integer.h"
17 
18 // ASM_REGISTER_STATE_CHECK(asm_function)
19 //   Minimally validates the environment pre & post function execution. This
20 //   variant should be used with assembly functions which are not expected to
21 //   fully restore the system state. See platform implementations of
22 //   RegisterStateCheck for details.
23 //
24 // API_REGISTER_STATE_CHECK(api_function)
25 //   Performs all the checks done by ASM_REGISTER_STATE_CHECK() and any
26 //   additional checks to ensure the environment is in a consistent state pre &
27 //   post function execution. This variant should be used with API functions.
28 //   See platform implementations of RegisterStateCheckXXX for details.
29 //
30 
31 #if defined(_WIN64)
32 
33 #undef NOMINMAX
34 #define NOMINMAX
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #include <winnt.h>
38 
39 namespace testing {
40 namespace internal {
41 
42 inline bool operator==(const M128A& lhs, const M128A& rhs) {
43   return (lhs.Low == rhs.Low && lhs.High == rhs.High);
44 }
45 
46 }  // namespace internal
47 }  // namespace testing
48 
49 namespace libvpx_test {
50 
51 // Compares the state of xmm[6-15] at construction with their state at
52 // destruction. These registers should be preserved by the callee on
53 // Windows x64.
54 class RegisterStateCheck {
55  public:
RegisterStateCheck()56   RegisterStateCheck() { initialized_ = StoreRegisters(&pre_context_); }
~RegisterStateCheck()57   ~RegisterStateCheck() { EXPECT_TRUE(Check()); }
58 
59  private:
StoreRegisters(CONTEXT * const context)60   static bool StoreRegisters(CONTEXT* const context) {
61     const HANDLE this_thread = GetCurrentThread();
62     EXPECT_TRUE(this_thread != NULL);
63     context->ContextFlags = CONTEXT_FLOATING_POINT;
64     const bool context_saved = GetThreadContext(this_thread, context) == TRUE;
65     EXPECT_TRUE(context_saved) << "GetLastError: " << GetLastError();
66     return context_saved;
67   }
68 
69   // Compares the register state. Returns true if the states match.
Check()70   bool Check() const {
71     if (!initialized_) return false;
72     CONTEXT post_context;
73     if (!StoreRegisters(&post_context)) return false;
74 
75     const M128A* xmm_pre = &pre_context_.Xmm6;
76     const M128A* xmm_post = &post_context.Xmm6;
77     for (int i = 6; i <= 15; ++i) {
78       EXPECT_EQ(*xmm_pre, *xmm_post) << "xmm" << i << " has been modified!";
79       ++xmm_pre;
80       ++xmm_post;
81     }
82     return !testing::Test::HasNonfatalFailure();
83   }
84 
85   bool initialized_;
86   CONTEXT pre_context_;
87 };
88 
89 #define ASM_REGISTER_STATE_CHECK(statement) do {  \
90   libvpx_test::RegisterStateCheck reg_check;      \
91   statement;                                      \
92 } while (false)
93 
94 }  // namespace libvpx_test
95 
96 #elif defined(CONFIG_SHARED) && defined(HAVE_NEON_ASM) && defined(CONFIG_VP9) \
97       && !CONFIG_SHARED && HAVE_NEON_ASM && CONFIG_VP9
98 
99 extern "C" {
100 // Save the d8-d15 registers into store.
101 void vpx_push_neon(int64_t *store);
102 }
103 
104 namespace libvpx_test {
105 
106 // Compares the state of d8-d15 at construction with their state at
107 // destruction. These registers should be preserved by the callee on
108 // arm platform.
109 class RegisterStateCheck {
110  public:
RegisterStateCheck()111   RegisterStateCheck() { initialized_ = StoreRegisters(pre_store_); }
~RegisterStateCheck()112   ~RegisterStateCheck() { EXPECT_TRUE(Check()); }
113 
114  private:
StoreRegisters(int64_t store[8])115   static bool StoreRegisters(int64_t store[8]) {
116     vpx_push_neon(store);
117     return true;
118   }
119 
120   // Compares the register state. Returns true if the states match.
Check()121   bool Check() const {
122     if (!initialized_) return false;
123     int64_t post_store[8];
124     vpx_push_neon(post_store);
125     for (int i = 0; i < 8; ++i) {
126       EXPECT_EQ(pre_store_[i], post_store[i]) << "d"
127           << i + 8 << " has been modified";
128     }
129     return !testing::Test::HasNonfatalFailure();
130   }
131 
132   bool initialized_;
133   int64_t pre_store_[8];
134 };
135 
136 #define ASM_REGISTER_STATE_CHECK(statement) do {  \
137   libvpx_test::RegisterStateCheck reg_check;      \
138   statement;                                      \
139 } while (false)
140 
141 }  // namespace libvpx_test
142 
143 #else
144 
145 namespace libvpx_test {
146 
147 class RegisterStateCheck {};
148 #define ASM_REGISTER_STATE_CHECK(statement) statement
149 
150 }  // namespace libvpx_test
151 
152 #endif  // _WIN64
153 
154 #if ARCH_X86 || ARCH_X86_64
155 #if defined(__GNUC__)
156 
157 namespace libvpx_test {
158 
159 // Checks the FPU tag word pre/post execution to ensure emms has been called.
160 class RegisterStateCheckMMX {
161  public:
RegisterStateCheckMMX()162   RegisterStateCheckMMX() {
163     __asm__ volatile("fstenv %0" : "=rm"(pre_fpu_env_));
164   }
~RegisterStateCheckMMX()165   ~RegisterStateCheckMMX() { EXPECT_TRUE(Check()); }
166 
167  private:
168   // Checks the FPU tag word pre/post execution, returning false if not cleared
169   // to 0xffff.
Check()170   bool Check() const {
171     EXPECT_EQ(0xffff, pre_fpu_env_[4])
172         << "FPU was in an inconsistent state prior to call";
173 
174     uint16_t post_fpu_env[14];
175     __asm__ volatile("fstenv %0" : "=rm"(post_fpu_env));
176     EXPECT_EQ(0xffff, post_fpu_env[4])
177         << "FPU was left in an inconsistent state after call";
178     return !testing::Test::HasNonfatalFailure();
179   }
180 
181   uint16_t pre_fpu_env_[14];
182 };
183 
184 #define API_REGISTER_STATE_CHECK(statement) do {  \
185   libvpx_test::RegisterStateCheckMMX reg_check;   \
186   ASM_REGISTER_STATE_CHECK(statement);            \
187 } while (false)
188 
189 }  // namespace libvpx_test
190 
191 #endif  // __GNUC__
192 #endif  // ARCH_X86 || ARCH_X86_64
193 
194 #ifndef API_REGISTER_STATE_CHECK
195 #define API_REGISTER_STATE_CHECK ASM_REGISTER_STATE_CHECK
196 #endif
197 
198 #endif  // TEST_REGISTER_STATE_CHECK_H_
199