1 /* Private floating point rounding and exceptions handling.  390/s390x version.
2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #ifndef S390_FENV_PRIVATE_H
20 #define S390_FENV_PRIVATE_H 1
21 
22 #include <fenv.h>
23 #include <fenv_libc.h>
24 #include <fpu_control.h>
25 
26 static __always_inline void
libc_feholdexcept_s390(fenv_t * envp)27 libc_feholdexcept_s390 (fenv_t *envp)
28 {
29   fpu_control_t fpc, fpc_new;
30 
31   /* Store the environment.  */
32   _FPU_GETCW (fpc);
33   envp->__fpc = fpc;
34 
35   /* Clear the current exception flags and dxc field.
36      Hold from generating fpu exceptions temporarily.  */
37   fpc_new = fpc & ~(FPC_FLAGS_MASK | FPC_DXC_MASK | FPC_EXCEPTION_MASK);
38 
39   /* Only set new environment if it has changed.  */
40   if (fpc_new != fpc)
41     _FPU_SETCW (fpc_new);
42 }
43 
44 #define libc_feholdexcept  libc_feholdexcept_s390
45 #define libc_feholdexceptf libc_feholdexcept_s390
46 #define libc_feholdexceptl libc_feholdexcept_s390
47 
48 static __always_inline void
libc_fesetround_s390(int round)49 libc_fesetround_s390 (int round)
50 {
51   __asm__ __volatile__ ("srnm 0(%0)" : : "a" (round));
52 }
53 
54 #define libc_fesetround  libc_fesetround_s390
55 #define libc_fesetroundf libc_fesetround_s390
56 #define libc_fesetroundl libc_fesetround_s390
57 
58 static __always_inline void
libc_feholdexcept_setround_s390(fenv_t * envp,int r)59 libc_feholdexcept_setround_s390 (fenv_t *envp, int r)
60 {
61   fpu_control_t fpc, fpc_new;
62 
63   _FPU_GETCW (fpc);
64   envp->__fpc = fpc;
65 
66   /* Clear the current exception flags and dxc field.
67      Hold from generating fpu exceptions temporarily.
68      Reset rounding mode bits.  */
69   fpc_new = fpc & ~(FPC_FLAGS_MASK | FPC_DXC_MASK | FPC_EXCEPTION_MASK
70 		    | FPC_RM_MASK);
71 
72   /* Set new rounding mode.  */
73   fpc_new |= (r & FPC_RM_MASK);
74 
75   /* Only set new environment if it has changed.  */
76   if (fpc_new != fpc)
77     _FPU_SETCW (fpc_new);
78 }
79 
80 #define libc_feholdexcept_setround  libc_feholdexcept_setround_s390
81 #define libc_feholdexcept_setroundf libc_feholdexcept_setround_s390
82 #define libc_feholdexcept_setroundl libc_feholdexcept_setround_s390
83 
84 static __always_inline int
libc_fetestexcept_s390(int excepts)85 libc_fetestexcept_s390 (int excepts)
86 {
87   int res;
88   fexcept_t fpc;
89 
90   _FPU_GETCW (fpc);
91 
92   /* Get current exceptions.  */
93   res = (fpc >> FPC_FLAGS_SHIFT) & FE_ALL_EXCEPT;
94   if ((fpc & FPC_NOT_FPU_EXCEPTION) == 0)
95     /* Bits 6, 7 of dxc-byte are zero,
96        thus bits 0-5 of dxc-byte correspond to the flag-bits.
97        Evaluate flags and last dxc-exception-code.  */
98     res |= (fpc >> FPC_DXC_SHIFT) & FE_ALL_EXCEPT;
99 
100   return res & excepts;
101 }
102 
103 #define libc_fetestexcept  libc_fetestexcept_s390
104 #define libc_fetestexceptf libc_fetestexcept_s390
105 #define libc_fetestexceptl libc_fetestexcept_s390
106 
107 static __always_inline void
libc_fesetenv_s390(const fenv_t * envp)108 libc_fesetenv_s390 (const fenv_t *envp)
109 {
110   _FPU_SETCW (envp->__fpc);
111 }
112 
113 #define libc_fesetenv  libc_fesetenv_s390
114 #define libc_fesetenvf libc_fesetenv_s390
115 #define libc_fesetenvl libc_fesetenv_s390
116 
117 static __always_inline int
libc_feupdateenv_test_s390(const fenv_t * envp,int ex)118 libc_feupdateenv_test_s390 (const fenv_t *envp, int ex)
119 {
120   /* Get the currently raised exceptions.  */
121   int excepts;
122   fexcept_t fpc_old;
123 
124   _FPU_GETCW (fpc_old);
125 
126   /* Get current exceptions.  */
127   excepts = (fpc_old >> FPC_FLAGS_SHIFT) & FE_ALL_EXCEPT;
128   if ((fpc_old & FPC_NOT_FPU_EXCEPTION) == 0)
129     /* Bits 6, 7 of dxc-byte are zero,
130        thus bits 0-5 of dxc-byte correspond to the flag-bits.
131        Evaluate flags and last dxc-exception-code.  */
132     excepts |= (fpc_old >> FPC_DXC_SHIFT) & FE_ALL_EXCEPT;
133 
134   /* Merge the currently raised exceptions with those in envp.  */
135   fpu_control_t fpc_new = envp->__fpc;
136   fpc_new |= excepts << FPC_FLAGS_SHIFT;
137 
138   /* Install the new fpc from envp.  */
139   if (fpc_new != fpc_old)
140     _FPU_SETCW (fpc_new);
141 
142   /* Raise the exceptions if enabled in new fpc.  */
143   if (__glibc_unlikely ((fpc_new >> FPC_EXCEPTION_MASK_SHIFT) & excepts))
144     __feraiseexcept (excepts);
145 
146   return excepts & ex;
147 }
148 
149 #define libc_feupdateenv_test  libc_feupdateenv_test_s390
150 #define libc_feupdateenv_testf libc_feupdateenv_test_s390
151 #define libc_feupdateenv_testl libc_feupdateenv_test_s390
152 
153 static __always_inline void
libc_feupdateenv_s390(const fenv_t * envp)154 libc_feupdateenv_s390 (const fenv_t *envp)
155 {
156   libc_feupdateenv_test_s390 (envp, 0);
157 }
158 
159 #define libc_feupdateenv  libc_feupdateenv_s390
160 #define libc_feupdateenvf libc_feupdateenv_s390
161 #define libc_feupdateenvl libc_feupdateenv_s390
162 
163 static __always_inline fenv_t
libc_handle_user_fenv_s390(const fenv_t * envp)164 libc_handle_user_fenv_s390 (const fenv_t *envp)
165 {
166   fenv_t env;
167   if (envp == FE_DFL_ENV)
168     {
169       env.__fpc = _FPU_DEFAULT;
170     }
171   else if (envp == FE_NOMASK_ENV)
172     {
173       env.__fpc = FPC_EXCEPTION_MASK;
174     }
175   else
176     env = (*envp);
177 
178   return env;
179 }
180 
181 /* We have support for rounding mode context.  */
182 #define HAVE_RM_CTX 1
183 
184 static __always_inline void
libc_feholdsetround_s390_ctx(struct rm_ctx * ctx,int r)185 libc_feholdsetround_s390_ctx (struct rm_ctx *ctx, int r)
186 {
187   fpu_control_t fpc;
188   int round;
189 
190   _FPU_GETCW (fpc);
191   ctx->env.__fpc = fpc;
192 
193   /* Check whether rounding modes are different.  */
194   round = fpc & FPC_RM_MASK;
195 
196   /* Set the rounding mode if changed.  */
197   if (__glibc_unlikely (round != r))
198     {
199       ctx->updated_status = true;
200       libc_fesetround_s390 (r);
201     }
202   else
203     ctx->updated_status = false;
204 }
205 
206 #define libc_feholdsetround_ctx		libc_feholdsetround_s390_ctx
207 #define libc_feholdsetroundf_ctx	libc_feholdsetround_s390_ctx
208 #define libc_feholdsetroundl_ctx	libc_feholdsetround_s390_ctx
209 
210 static __always_inline void
libc_feresetround_s390_ctx(struct rm_ctx * ctx)211 libc_feresetround_s390_ctx (struct rm_ctx *ctx)
212 {
213   /* Restore the rounding mode if updated.  */
214   if (__glibc_unlikely (ctx->updated_status))
215     {
216       fpu_control_t fpc;
217       _FPU_GETCW (fpc);
218       fpc = ctx->env.__fpc | (fpc & FPC_FLAGS_MASK);
219       _FPU_SETCW (fpc);
220     }
221 }
222 
223 #define libc_feresetround_ctx		libc_feresetround_s390_ctx
224 #define libc_feresetroundf_ctx		libc_feresetround_s390_ctx
225 #define libc_feresetroundl_ctx		libc_feresetround_s390_ctx
226 
227 static __always_inline void
libc_feholdsetround_noex_s390_ctx(struct rm_ctx * ctx,int r)228 libc_feholdsetround_noex_s390_ctx (struct rm_ctx *ctx, int r)
229 {
230   libc_feholdexcept_setround_s390 (&ctx->env, r);
231 }
232 
233 #define libc_feholdsetround_noex_ctx	libc_feholdsetround_noex_s390_ctx
234 #define libc_feholdsetround_noexf_ctx	libc_feholdsetround_noex_s390_ctx
235 #define libc_feholdsetround_noexl_ctx	libc_feholdsetround_noex_s390_ctx
236 
237 static __always_inline void
libc_feresetround_noex_s390_ctx(struct rm_ctx * ctx)238 libc_feresetround_noex_s390_ctx (struct rm_ctx *ctx)
239 {
240   /* Restore exception flags and rounding mode.  */
241   libc_fesetenv_s390 (&ctx->env);
242 }
243 
244 #define libc_feresetround_noex_ctx	libc_feresetround_noex_s390_ctx
245 #define libc_feresetround_noexf_ctx	libc_feresetround_noex_s390_ctx
246 #define libc_feresetround_noexl_ctx	libc_feresetround_noex_s390_ctx
247 
248 #include_next <fenv_private.h>
249 
250 #endif
251