xref: /oneTBB/test/common/fp_control.h (revision 4523a761)
1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef __TBB_test_common_fp_control_H_
18 #define __TBB_test_common_fp_control_H_
19 
20 #include "common/test.h"
21 #include "oneapi/tbb/detail/_machine.h"
22 
23 #if ( __TBB_x86_32 || __TBB_x86_64 )
24 
25 const int
26 #if _WIN32 || _WIN64
27             FE_TONEAREST = _RC_NEAR,
28             FE_DOWNWARD = _RC_DOWN,
29             FE_UPWARD = _RC_UP,
30             FE_TOWARDZERO = _RC_CHOP,
31             SSE_SHIFT = 5,
32 #else
33             FE_TONEAREST = 0x0000,
34             FE_DOWNWARD = 0x0400,
35             FE_UPWARD = 0x0800,
36             FE_TOWARDZERO = 0x0c00,
37             SSE_SHIFT = 3,
38 #endif
39             FE_RND_MODE_MASK = FE_TOWARDZERO,
40             SSE_RND_MODE_MASK = FE_RND_MODE_MASK << SSE_SHIFT,
41             SSE_DAZ = 0x0040,
42             SSE_FTZ = 0x8000,
43             SSE_MODE_MASK = SSE_DAZ | SSE_FTZ,
44             SSE_STATUS_MASK = 0x3F;
45 
46 const int NumSseModes = 4;
47 const int SseModes[NumSseModes] = { 0, SSE_DAZ, SSE_FTZ, SSE_DAZ | SSE_FTZ };
48 
49 inline int GetRoundingMode ( bool checkConsistency = true ) {
50     tbb::detail::d1::cpu_ctl_env ctl;
51     ctl.get_env();
52     if (checkConsistency) {
53         auto sse_rnd_mode = (ctl.mxcsr & SSE_RND_MODE_MASK) >> SSE_SHIFT;
54         auto x87_rnd_mode = (ctl.x87cw & FE_RND_MODE_MASK);
55         CHECK(sse_rnd_mode == x87_rnd_mode);
56     }
57     return ctl.x87cw & FE_RND_MODE_MASK;
58 }
59 
SetRoundingMode(int mode)60 inline void SetRoundingMode ( int mode ) {
61     tbb::detail::d1::cpu_ctl_env ctl;
62     ctl.get_env();
63     ctl.mxcsr = (ctl.mxcsr & ~SSE_RND_MODE_MASK) | (mode & FE_RND_MODE_MASK) << SSE_SHIFT;
64     ctl.x87cw = ((ctl.x87cw & ~FE_RND_MODE_MASK) | (mode & FE_RND_MODE_MASK));
65 
66     if (true) {
67         auto sse_rnd_mode = (ctl.mxcsr & SSE_RND_MODE_MASK) >> SSE_SHIFT;
68         auto x87_rnd_mode = (ctl.x87cw & FE_RND_MODE_MASK);
69         CHECK(sse_rnd_mode == x87_rnd_mode);
70     }
71 
72     ctl.set_env();
73 }
74 
GetSseMode()75 inline int GetSseMode () {
76     tbb::detail::d1::cpu_ctl_env ctl;
77     ctl.get_env();
78     return ctl.mxcsr & SSE_MODE_MASK;
79 }
80 
SetSseMode(int mode)81 inline void SetSseMode ( int mode ) {
82     tbb::detail::d1::cpu_ctl_env ctl;
83     ctl.get_env();
84     ctl.mxcsr = (ctl.mxcsr & ~SSE_MODE_MASK) | (mode & SSE_MODE_MASK);
85     ctl.set_env();
86 }
87 
88 #elif defined(_M_ARM)
89 const int NumSseModes = 1;
90 const int SseModes[NumSseModes] = { 0 };
91 
GetSseMode()92 inline int GetSseMode () { return 0; }
SetSseMode(int)93 inline void SetSseMode ( int ) {}
94 
95 const int FE_TONEAREST = _RC_NEAR,
96           FE_DOWNWARD = _RC_DOWN,
97           FE_UPWARD = _RC_UP,
98           FE_TOWARDZERO = _RC_CHOP;
99 
100 inline int GetRoundingMode ( bool = true ) {
101     tbb::detail::d1::cpu_ctl_env ctl;
102     ctl.get_env();
103     return ctl.my_ctl;
104 }
SetRoundingMode(int mode)105 inline void SetRoundingMode ( int mode ) {
106     tbb::detail::d1::cpu_ctl_env ctl;
107     ctl.my_ctl = mode;
108     ctl.set_env();
109 }
110 
111 #else /* Other archs */
112 
113 #include <fenv.h>
114 
115 const int RND_MODE_MASK = FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO;
116 
117 const int NumSseModes = 1;
118 const int SseModes[NumSseModes] = { 0 };
119 
120 inline int GetRoundingMode ( bool = true ) { return fegetround(); }
SetRoundingMode(int rnd)121 inline void SetRoundingMode ( int rnd ) { fesetround(rnd); }
122 
GetSseMode()123 inline int GetSseMode () { return 0; }
SetSseMode(int)124 inline void SetSseMode ( int ) {}
125 
126 #endif /* Other archs */
127 
128 const int NumRoundingModes = 4;
129 const int RoundingModes[NumRoundingModes] = { FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO };
130 const int numFPModes = NumRoundingModes*NumSseModes;
131 
SetFPMode(int mode)132 inline void SetFPMode( int mode ) {
133     SetRoundingMode( RoundingModes[mode/NumSseModes%NumRoundingModes] );
134     SetSseMode( SseModes[mode%NumSseModes] );
135 }
136 
137 #define AssertFPMode( mode ) { \
138     CHECK_MESSAGE( GetRoundingMode() == RoundingModes[mode/NumSseModes%NumRoundingModes], "FPU control state has not been set correctly." ); \
139     CHECK_MESSAGE( GetSseMode() == SseModes[mode%NumSseModes], "SSE control state has not been set correctly." ); \
140 }
141 
142 inline int SetNextFPMode( int mode, int step = 1 ) {
143     const int nextMode = (mode+step)%numFPModes;
144     SetFPMode( nextMode );
145     return nextMode;
146 }
147 
148 class FPModeContext {
149     int origSse, origRounding;
150     int currentMode;
151 public:
FPModeContext(int newMode)152     FPModeContext(int newMode) {
153         origSse = GetSseMode();
154         origRounding = GetRoundingMode();
155         SetFPMode(currentMode = newMode);
156     }
~FPModeContext()157     ~FPModeContext() {
158         assertFPMode();
159         SetRoundingMode(origRounding);
160         SetSseMode(origSse);
161     }
setNextFPMode()162     int setNextFPMode() {
163         assertFPMode();
164         return currentMode = SetNextFPMode(currentMode);
165     }
assertFPMode()166     void assertFPMode() {
167         AssertFPMode(currentMode);
168     }
169 };
170 #endif // __TBB_test_common_memory_usagae_H_
171