fenv_private.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. #ifndef X86_FENV_PRIVATE_H
  2. #define X86_FENV_PRIVATE_H 1
  3. #include <bits/floatn.h>
  4. #include <fenv.h>
  5. #include <fpu_control.h>
  6. /* This file is used by both the 32- and 64-bit ports. The 64-bit port
  7. has a field in the fenv_t for the mxcsr; the 32-bit port does not.
  8. Instead, we (ab)use the only 32-bit field extant in the struct. */
  9. #ifndef __x86_64__
  10. # define __mxcsr __eip
  11. #endif
  12. /* All of these functions are private to libm, and are all used in pairs
  13. to save+change the fp state and restore the original state. Thus we
  14. need not care for both the 387 and the sse unit, only the one we're
  15. actually using. */
  16. #if defined __AVX__ || defined SSE2AVX
  17. # define STMXCSR "vstmxcsr"
  18. # define LDMXCSR "vldmxcsr"
  19. #else
  20. # define STMXCSR "stmxcsr"
  21. # define LDMXCSR "ldmxcsr"
  22. #endif
  23. static __always_inline void
  24. libc_feholdexcept_sse (fenv_t *e)
  25. {
  26. unsigned int mxcsr;
  27. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  28. e->__mxcsr = mxcsr;
  29. mxcsr = (mxcsr | 0x1f80) & ~0x3f;
  30. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  31. }
  32. static __always_inline void
  33. libc_feholdexcept_387 (fenv_t *e)
  34. {
  35. /* Recall that fnstenv has a side-effect of masking exceptions.
  36. Clobber all of the fp registers so that the TOS field is 0. */
  37. asm volatile ("fnstenv %0; fnclex"
  38. : "=m"(*e)
  39. : : "st", "st(1)", "st(2)", "st(3)",
  40. "st(4)", "st(5)", "st(6)", "st(7)");
  41. }
  42. static __always_inline void
  43. libc_fesetround_sse (int r)
  44. {
  45. unsigned int mxcsr;
  46. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  47. mxcsr = (mxcsr & ~0x6000) | (r << 3);
  48. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  49. }
  50. static __always_inline void
  51. libc_fesetround_387 (int r)
  52. {
  53. fpu_control_t cw;
  54. _FPU_GETCW (cw);
  55. cw = (cw & ~0xc00) | r;
  56. _FPU_SETCW (cw);
  57. }
  58. static __always_inline void
  59. libc_feholdexcept_setround_sse (fenv_t *e, int r)
  60. {
  61. unsigned int mxcsr;
  62. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  63. e->__mxcsr = mxcsr;
  64. mxcsr = ((mxcsr | 0x1f80) & ~0x603f) | (r << 3);
  65. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  66. }
  67. /* Set both rounding mode and precision. A convenience function for use
  68. by libc_feholdexcept_setround and libc_feholdexcept_setround_53bit. */
  69. static __always_inline void
  70. libc_feholdexcept_setround_387_prec (fenv_t *e, int r)
  71. {
  72. libc_feholdexcept_387 (e);
  73. fpu_control_t cw = e->__control_word;
  74. cw &= ~(_FPU_RC_ZERO | _FPU_EXTENDED);
  75. cw |= r | 0x3f;
  76. _FPU_SETCW (cw);
  77. }
  78. static __always_inline void
  79. libc_feholdexcept_setround_387 (fenv_t *e, int r)
  80. {
  81. libc_feholdexcept_setround_387_prec (e, r | _FPU_EXTENDED);
  82. }
  83. static __always_inline void
  84. libc_feholdexcept_setround_387_53bit (fenv_t *e, int r)
  85. {
  86. libc_feholdexcept_setround_387_prec (e, r | _FPU_DOUBLE);
  87. }
  88. static __always_inline int
  89. libc_fetestexcept_sse (int e)
  90. {
  91. unsigned int mxcsr;
  92. asm volatile (STMXCSR " %0" : "=m" (*&mxcsr));
  93. return mxcsr & e & FE_ALL_EXCEPT;
  94. }
  95. static __always_inline int
  96. libc_fetestexcept_387 (int ex)
  97. {
  98. fexcept_t temp;
  99. asm volatile ("fnstsw %0" : "=a" (temp));
  100. return temp & ex & FE_ALL_EXCEPT;
  101. }
  102. static __always_inline void
  103. libc_fesetenv_sse (fenv_t *e)
  104. {
  105. asm volatile (LDMXCSR " %0" : : "m" (e->__mxcsr));
  106. }
  107. static __always_inline void
  108. libc_fesetenv_387 (fenv_t *e)
  109. {
  110. /* Clobber all fp registers so that the TOS value we saved earlier is
  111. compatible with the current state of the compiler. */
  112. asm volatile ("fldenv %0"
  113. : : "m" (*e)
  114. : "st", "st(1)", "st(2)", "st(3)",
  115. "st(4)", "st(5)", "st(6)", "st(7)");
  116. }
  117. static __always_inline int
  118. libc_feupdateenv_test_sse (fenv_t *e, int ex)
  119. {
  120. unsigned int mxcsr, old_mxcsr, cur_ex;
  121. asm volatile (STMXCSR " %0" : "=m" (*&mxcsr));
  122. cur_ex = mxcsr & FE_ALL_EXCEPT;
  123. /* Merge current exceptions with the old environment. */
  124. old_mxcsr = e->__mxcsr;
  125. mxcsr = old_mxcsr | cur_ex;
  126. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  127. /* Raise SIGFPE for any new exceptions since the hold. Expect that
  128. the normal environment has all exceptions masked. */
  129. if (__glibc_unlikely (~(old_mxcsr >> 7) & cur_ex))
  130. __feraiseexcept (cur_ex);
  131. /* Test for exceptions raised since the hold. */
  132. return cur_ex & ex;
  133. }
  134. static __always_inline int
  135. libc_feupdateenv_test_387 (fenv_t *e, int ex)
  136. {
  137. fexcept_t cur_ex;
  138. /* Save current exceptions. */
  139. asm volatile ("fnstsw %0" : "=a" (cur_ex));
  140. cur_ex &= FE_ALL_EXCEPT;
  141. /* Reload original environment. */
  142. libc_fesetenv_387 (e);
  143. /* Merge current exceptions. */
  144. __feraiseexcept (cur_ex);
  145. /* Test for exceptions raised since the hold. */
  146. return cur_ex & ex;
  147. }
  148. static __always_inline void
  149. libc_feupdateenv_sse (fenv_t *e)
  150. {
  151. libc_feupdateenv_test_sse (e, 0);
  152. }
  153. static __always_inline void
  154. libc_feupdateenv_387 (fenv_t *e)
  155. {
  156. libc_feupdateenv_test_387 (e, 0);
  157. }
  158. static __always_inline void
  159. libc_feholdsetround_sse (fenv_t *e, int r)
  160. {
  161. unsigned int mxcsr;
  162. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  163. e->__mxcsr = mxcsr;
  164. mxcsr = (mxcsr & ~0x6000) | (r << 3);
  165. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  166. }
  167. static __always_inline void
  168. libc_feholdsetround_387_prec (fenv_t *e, int r)
  169. {
  170. fpu_control_t cw;
  171. _FPU_GETCW (cw);
  172. e->__control_word = cw;
  173. cw &= ~(_FPU_RC_ZERO | _FPU_EXTENDED);
  174. cw |= r;
  175. _FPU_SETCW (cw);
  176. }
  177. static __always_inline void
  178. libc_feholdsetround_387 (fenv_t *e, int r)
  179. {
  180. libc_feholdsetround_387_prec (e, r | _FPU_EXTENDED);
  181. }
  182. static __always_inline void
  183. libc_feholdsetround_387_53bit (fenv_t *e, int r)
  184. {
  185. libc_feholdsetround_387_prec (e, r | _FPU_DOUBLE);
  186. }
  187. static __always_inline void
  188. libc_feresetround_sse (fenv_t *e)
  189. {
  190. unsigned int mxcsr;
  191. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  192. mxcsr = (mxcsr & ~0x6000) | (e->__mxcsr & 0x6000);
  193. asm volatile (LDMXCSR " %0" : : "m" (*&mxcsr));
  194. }
  195. static __always_inline void
  196. libc_feresetround_387 (fenv_t *e)
  197. {
  198. _FPU_SETCW (e->__control_word);
  199. }
  200. #ifdef __SSE_MATH__
  201. # define libc_feholdexceptf libc_feholdexcept_sse
  202. # define libc_fesetroundf libc_fesetround_sse
  203. # define libc_feholdexcept_setroundf libc_feholdexcept_setround_sse
  204. # define libc_fetestexceptf libc_fetestexcept_sse
  205. # define libc_fesetenvf libc_fesetenv_sse
  206. # define libc_feupdateenv_testf libc_feupdateenv_test_sse
  207. # define libc_feupdateenvf libc_feupdateenv_sse
  208. # define libc_feholdsetroundf libc_feholdsetround_sse
  209. # define libc_feresetroundf libc_feresetround_sse
  210. #else
  211. # define libc_feholdexceptf libc_feholdexcept_387
  212. # define libc_fesetroundf libc_fesetround_387
  213. # define libc_feholdexcept_setroundf libc_feholdexcept_setround_387
  214. # define libc_fetestexceptf libc_fetestexcept_387
  215. # define libc_fesetenvf libc_fesetenv_387
  216. # define libc_feupdateenv_testf libc_feupdateenv_test_387
  217. # define libc_feupdateenvf libc_feupdateenv_387
  218. # define libc_feholdsetroundf libc_feholdsetround_387
  219. # define libc_feresetroundf libc_feresetround_387
  220. #endif /* __SSE_MATH__ */
  221. #ifdef __SSE2_MATH__
  222. # define libc_feholdexcept libc_feholdexcept_sse
  223. # define libc_fesetround libc_fesetround_sse
  224. # define libc_feholdexcept_setround libc_feholdexcept_setround_sse
  225. # define libc_fetestexcept libc_fetestexcept_sse
  226. # define libc_fesetenv libc_fesetenv_sse
  227. # define libc_feupdateenv_test libc_feupdateenv_test_sse
  228. # define libc_feupdateenv libc_feupdateenv_sse
  229. # define libc_feholdsetround libc_feholdsetround_sse
  230. # define libc_feresetround libc_feresetround_sse
  231. #else
  232. # define libc_feholdexcept libc_feholdexcept_387
  233. # define libc_fesetround libc_fesetround_387
  234. # define libc_feholdexcept_setround libc_feholdexcept_setround_387
  235. # define libc_fetestexcept libc_fetestexcept_387
  236. # define libc_fesetenv libc_fesetenv_387
  237. # define libc_feupdateenv_test libc_feupdateenv_test_387
  238. # define libc_feupdateenv libc_feupdateenv_387
  239. # define libc_feholdsetround libc_feholdsetround_387
  240. # define libc_feresetround libc_feresetround_387
  241. #endif /* __SSE2_MATH__ */
  242. #define libc_feholdexceptl libc_feholdexcept_387
  243. #define libc_fesetroundl libc_fesetround_387
  244. #define libc_feholdexcept_setroundl libc_feholdexcept_setround_387
  245. #define libc_fetestexceptl libc_fetestexcept_387
  246. #define libc_fesetenvl libc_fesetenv_387
  247. #define libc_feupdateenv_testl libc_feupdateenv_test_387
  248. #define libc_feupdateenvl libc_feupdateenv_387
  249. #define libc_feholdsetroundl libc_feholdsetround_387
  250. #define libc_feresetroundl libc_feresetround_387
  251. #ifndef __SSE2_MATH__
  252. # define libc_feholdexcept_setround_53bit libc_feholdexcept_setround_387_53bit
  253. # define libc_feholdsetround_53bit libc_feholdsetround_387_53bit
  254. #endif
  255. #ifdef __x86_64__
  256. /* The SSE rounding mode is used by soft-fp (libgcc and glibc) on
  257. x86_64, so that must be set for float128 computations. */
  258. # define SET_RESTORE_ROUNDF128(RM) \
  259. SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_sse, libc_feresetround_sse)
  260. # define libc_feholdexcept_setroundf128 libc_feholdexcept_setround_sse
  261. # define libc_feupdateenv_testf128 libc_feupdateenv_test_sse
  262. #else
  263. /* The 387 rounding mode is used by soft-fp for 32-bit, but whether
  264. 387 or SSE exceptions are used depends on whether libgcc was built
  265. for SSE math, which is not known when glibc is being built. */
  266. # define libc_feholdexcept_setroundf128 default_libc_feholdexcept_setround
  267. # define libc_feupdateenv_testf128 default_libc_feupdateenv_test
  268. #endif
  269. /* We have support for rounding mode context. */
  270. #define HAVE_RM_CTX 1
  271. static __always_inline void
  272. libc_feholdexcept_setround_sse_ctx (struct rm_ctx *ctx, int r)
  273. {
  274. unsigned int mxcsr, new_mxcsr;
  275. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  276. new_mxcsr = ((mxcsr | 0x1f80) & ~0x603f) | (r << 3);
  277. ctx->env.__mxcsr = mxcsr;
  278. if (__glibc_unlikely (mxcsr != new_mxcsr))
  279. {
  280. asm volatile (LDMXCSR " %0" : : "m" (*&new_mxcsr));
  281. ctx->updated_status = true;
  282. }
  283. else
  284. ctx->updated_status = false;
  285. }
  286. /* Unconditional since we want to overwrite any exceptions that occurred in the
  287. context. This is also why all fehold* functions unconditionally write into
  288. ctx->env. */
  289. static __always_inline void
  290. libc_fesetenv_sse_ctx (struct rm_ctx *ctx)
  291. {
  292. libc_fesetenv_sse (&ctx->env);
  293. }
  294. static __always_inline void
  295. libc_feupdateenv_sse_ctx (struct rm_ctx *ctx)
  296. {
  297. if (__glibc_unlikely (ctx->updated_status))
  298. libc_feupdateenv_test_sse (&ctx->env, 0);
  299. }
  300. static __always_inline void
  301. libc_feholdexcept_setround_387_prec_ctx (struct rm_ctx *ctx, int r)
  302. {
  303. libc_feholdexcept_387 (&ctx->env);
  304. fpu_control_t cw = ctx->env.__control_word;
  305. fpu_control_t old_cw = cw;
  306. cw &= ~(_FPU_RC_ZERO | _FPU_EXTENDED);
  307. cw |= r | 0x3f;
  308. if (__glibc_unlikely (old_cw != cw))
  309. {
  310. _FPU_SETCW (cw);
  311. ctx->updated_status = true;
  312. }
  313. else
  314. ctx->updated_status = false;
  315. }
  316. static __always_inline void
  317. libc_feholdexcept_setround_387_ctx (struct rm_ctx *ctx, int r)
  318. {
  319. libc_feholdexcept_setround_387_prec_ctx (ctx, r | _FPU_EXTENDED);
  320. }
  321. static __always_inline void
  322. libc_feholdexcept_setround_387_53bit_ctx (struct rm_ctx *ctx, int r)
  323. {
  324. libc_feholdexcept_setround_387_prec_ctx (ctx, r | _FPU_DOUBLE);
  325. }
  326. static __always_inline void
  327. libc_feholdsetround_387_prec_ctx (struct rm_ctx *ctx, int r)
  328. {
  329. fpu_control_t cw, new_cw;
  330. _FPU_GETCW (cw);
  331. new_cw = cw;
  332. new_cw &= ~(_FPU_RC_ZERO | _FPU_EXTENDED);
  333. new_cw |= r;
  334. ctx->env.__control_word = cw;
  335. if (__glibc_unlikely (new_cw != cw))
  336. {
  337. _FPU_SETCW (new_cw);
  338. ctx->updated_status = true;
  339. }
  340. else
  341. ctx->updated_status = false;
  342. }
  343. static __always_inline void
  344. libc_feholdsetround_387_ctx (struct rm_ctx *ctx, int r)
  345. {
  346. libc_feholdsetround_387_prec_ctx (ctx, r | _FPU_EXTENDED);
  347. }
  348. static __always_inline void
  349. libc_feholdsetround_387_53bit_ctx (struct rm_ctx *ctx, int r)
  350. {
  351. libc_feholdsetround_387_prec_ctx (ctx, r | _FPU_DOUBLE);
  352. }
  353. static __always_inline void
  354. libc_feholdsetround_sse_ctx (struct rm_ctx *ctx, int r)
  355. {
  356. unsigned int mxcsr, new_mxcsr;
  357. asm (STMXCSR " %0" : "=m" (*&mxcsr));
  358. new_mxcsr = (mxcsr & ~0x6000) | (r << 3);
  359. ctx->env.__mxcsr = mxcsr;
  360. if (__glibc_unlikely (new_mxcsr != mxcsr))
  361. {
  362. asm volatile (LDMXCSR " %0" : : "m" (*&new_mxcsr));
  363. ctx->updated_status = true;
  364. }
  365. else
  366. ctx->updated_status = false;
  367. }
  368. static __always_inline void
  369. libc_feresetround_sse_ctx (struct rm_ctx *ctx)
  370. {
  371. if (__glibc_unlikely (ctx->updated_status))
  372. libc_feresetround_sse (&ctx->env);
  373. }
  374. static __always_inline void
  375. libc_feresetround_387_ctx (struct rm_ctx *ctx)
  376. {
  377. if (__glibc_unlikely (ctx->updated_status))
  378. _FPU_SETCW (ctx->env.__control_word);
  379. }
  380. static __always_inline void
  381. libc_feupdateenv_387_ctx (struct rm_ctx *ctx)
  382. {
  383. if (__glibc_unlikely (ctx->updated_status))
  384. libc_feupdateenv_test_387 (&ctx->env, 0);
  385. }
  386. #ifdef __SSE_MATH__
  387. # define libc_feholdexcept_setroundf_ctx libc_feholdexcept_setround_sse_ctx
  388. # define libc_fesetenvf_ctx libc_fesetenv_sse_ctx
  389. # define libc_feupdateenvf_ctx libc_feupdateenv_sse_ctx
  390. # define libc_feholdsetroundf_ctx libc_feholdsetround_sse_ctx
  391. # define libc_feresetroundf_ctx libc_feresetround_sse_ctx
  392. #else
  393. # define libc_feholdexcept_setroundf_ctx libc_feholdexcept_setround_387_ctx
  394. # define libc_feupdateenvf_ctx libc_feupdateenv_387_ctx
  395. # define libc_feholdsetroundf_ctx libc_feholdsetround_387_ctx
  396. # define libc_feresetroundf_ctx libc_feresetround_387_ctx
  397. #endif /* __SSE_MATH__ */
  398. #ifdef __SSE2_MATH__
  399. # if defined (__x86_64__) || !defined (MATH_SET_BOTH_ROUNDING_MODES)
  400. # define libc_feholdexcept_setround_ctx libc_feholdexcept_setround_sse_ctx
  401. # define libc_fesetenv_ctx libc_fesetenv_sse_ctx
  402. # define libc_feupdateenv_ctx libc_feupdateenv_sse_ctx
  403. # define libc_feholdsetround_ctx libc_feholdsetround_sse_ctx
  404. # define libc_feresetround_ctx libc_feresetround_sse_ctx
  405. # else
  406. # define libc_feholdexcept_setround_ctx default_libc_feholdexcept_setround_ctx
  407. # define libc_fesetenv_ctx default_libc_fesetenv_ctx
  408. # define libc_feupdateenv_ctx default_libc_feupdateenv_ctx
  409. # define libc_feholdsetround_ctx default_libc_feholdsetround_ctx
  410. # define libc_feresetround_ctx default_libc_feresetround_ctx
  411. # endif
  412. #else
  413. # define libc_feholdexcept_setround_ctx libc_feholdexcept_setround_387_ctx
  414. # define libc_feupdateenv_ctx libc_feupdateenv_387_ctx
  415. # define libc_feholdsetround_ctx libc_feholdsetround_387_ctx
  416. # define libc_feresetround_ctx libc_feresetround_387_ctx
  417. #endif /* __SSE2_MATH__ */
  418. #define libc_feholdexcept_setroundl_ctx libc_feholdexcept_setround_387_ctx
  419. #define libc_feupdateenvl_ctx libc_feupdateenv_387_ctx
  420. #define libc_feholdsetroundl_ctx libc_feholdsetround_387_ctx
  421. #define libc_feresetroundl_ctx libc_feresetround_387_ctx
  422. #ifndef __SSE2_MATH__
  423. # define libc_feholdsetround_53bit_ctx libc_feholdsetround_387_53bit_ctx
  424. # define libc_feresetround_53bit_ctx libc_feresetround_387_ctx
  425. #endif
  426. #undef __mxcsr
  427. #include_next <fenv_private.h>
  428. #endif /* X86_FENV_PRIVATE_H */