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#define _WIN32_LEAN_AND_MEAN
34#include <windows.h>
35#include <winnt.h>
36
37namespace testing {
38namespace internal {
39
40inline bool operator==(const M128A& lhs, const M128A& rhs) {
41  return (lhs.Low == rhs.Low && lhs.High == rhs.High);
42}
43
44}  // namespace internal
45}  // namespace testing
46
47namespace libvpx_test {
48
49// Compares the state of xmm[6-15] at construction with their state at
50// destruction. These registers should be preserved by the callee on
51// Windows x64.
52class RegisterStateCheck {
53 public:
54  RegisterStateCheck() { initialized_ = StoreRegisters(&pre_context_); }
55  ~RegisterStateCheck() { EXPECT_TRUE(Check()); }
56
57 private:
58  static bool StoreRegisters(CONTEXT* const context) {
59    const HANDLE this_thread = GetCurrentThread();
60    EXPECT_TRUE(this_thread != NULL);
61    context->ContextFlags = CONTEXT_FLOATING_POINT;
62    const bool context_saved = GetThreadContext(this_thread, context) == TRUE;
63    EXPECT_TRUE(context_saved) << "GetLastError: " << GetLastError();
64    return context_saved;
65  }
66
67  // Compares the register state. Returns true if the states match.
68  bool Check() const {
69    if (!initialized_) return false;
70    CONTEXT post_context;
71    if (!StoreRegisters(&post_context)) return false;
72
73    const M128A* xmm_pre = &pre_context_.Xmm6;
74    const M128A* xmm_post = &post_context.Xmm6;
75    for (int i = 6; i <= 15; ++i) {
76      EXPECT_EQ(*xmm_pre, *xmm_post) << "xmm" << i << " has been modified!";
77      ++xmm_pre;
78      ++xmm_post;
79    }
80    return !testing::Test::HasNonfatalFailure();
81  }
82
83  bool initialized_;
84  CONTEXT pre_context_;
85};
86
87#define ASM_REGISTER_STATE_CHECK(statement) do {  \
88  libvpx_test::RegisterStateCheck reg_check;      \
89  statement;                                      \
90} while (false)
91
92}  // namespace libvpx_test
93
94#elif defined(CONFIG_SHARED) && defined(HAVE_NEON_ASM) && defined(CONFIG_VP9) \
95      && !CONFIG_SHARED && HAVE_NEON_ASM && CONFIG_VP9
96
97extern "C" {
98// Save the d8-d15 registers into store.
99void vp9_push_neon(int64_t *store);
100}
101
102namespace libvpx_test {
103
104// Compares the state of d8-d15 at construction with their state at
105// destruction. These registers should be preserved by the callee on
106// arm platform.
107class RegisterStateCheck {
108 public:
109  RegisterStateCheck() { initialized_ = StoreRegisters(pre_store_); }
110  ~RegisterStateCheck() { EXPECT_TRUE(Check()); }
111
112 private:
113  static bool StoreRegisters(int64_t store[8]) {
114    vp9_push_neon(store);
115    return true;
116  }
117
118  // Compares the register state. Returns true if the states match.
119  bool Check() const {
120    if (!initialized_) return false;
121    int64_t post_store[8];
122    vp9_push_neon(post_store);
123    for (int i = 0; i < 8; ++i) {
124      EXPECT_EQ(pre_store_[i], post_store[i]) << "d"
125          << i + 8 << " has been modified";
126    }
127    return !testing::Test::HasNonfatalFailure();
128  }
129
130  bool initialized_;
131  int64_t pre_store_[8];
132};
133
134#define ASM_REGISTER_STATE_CHECK(statement) do {  \
135  libvpx_test::RegisterStateCheck reg_check;      \
136  statement;                                      \
137} while (false)
138
139}  // namespace libvpx_test
140
141#else
142
143namespace libvpx_test {
144
145class RegisterStateCheck {};
146#define ASM_REGISTER_STATE_CHECK(statement) statement
147
148}  // namespace libvpx_test
149
150#endif  // _WIN64
151
152#if ARCH_X86 || ARCH_X86_64
153#if defined(__GNUC__)
154
155namespace libvpx_test {
156
157// Checks the FPU tag word pre/post execution to ensure emms has been called.
158class RegisterStateCheckMMX {
159 public:
160  RegisterStateCheckMMX() {
161    __asm__ volatile("fstenv %0" : "=rm"(pre_fpu_env_));
162  }
163  ~RegisterStateCheckMMX() { EXPECT_TRUE(Check()); }
164
165 private:
166  // Checks the FPU tag word pre/post execution, returning false if not cleared
167  // to 0xffff.
168  bool Check() const {
169    EXPECT_EQ(0xffff, pre_fpu_env_[4])
170        << "FPU was in an inconsistent state prior to call";
171
172    uint16_t post_fpu_env[14];
173    __asm__ volatile("fstenv %0" : "=rm"(post_fpu_env));
174    EXPECT_EQ(0xffff, post_fpu_env[4])
175        << "FPU was left in an inconsistent state after call";
176    return !testing::Test::HasNonfatalFailure();
177  }
178
179  uint16_t pre_fpu_env_[14];
180};
181
182#define API_REGISTER_STATE_CHECK(statement) do {  \
183  libvpx_test::RegisterStateCheckMMX reg_check;   \
184  ASM_REGISTER_STATE_CHECK(statement);            \
185} while (false)
186
187}  // namespace libvpx_test
188
189#endif  // __GNUC__
190#endif  // ARCH_X86 || ARCH_X86_64
191
192#ifndef API_REGISTER_STATE_CHECK
193#define API_REGISTER_STATE_CHECK ASM_REGISTER_STATE_CHECK
194#endif
195
196#endif  // TEST_REGISTER_STATE_CHECK_H_
197