test_FCOMI.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // SPDX-License-Identifier: GPL-2.0
  2. #undef _GNU_SOURCE
  3. #define _GNU_SOURCE 1
  4. #undef __USE_GNU
  5. #define __USE_GNU 1
  6. #include <unistd.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <signal.h>
  11. #include <sys/types.h>
  12. #include <sys/select.h>
  13. #include <sys/time.h>
  14. #include <sys/wait.h>
  15. #include <fenv.h>
  16. enum {
  17. CF = 1 << 0,
  18. PF = 1 << 2,
  19. ZF = 1 << 6,
  20. ARITH = CF | PF | ZF,
  21. };
  22. long res_fcomi_pi_1;
  23. long res_fcomi_1_pi;
  24. long res_fcomi_1_1;
  25. long res_fcomi_nan_1;
  26. /* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */
  27. /* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */
  28. int snan = 0x7fc11111;
  29. int qnan = 0x7f811111;
  30. unsigned short snan1[5];
  31. /* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */
  32. unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff };
  33. int test(long flags)
  34. {
  35. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  36. asm ("\n"
  37. " push %0""\n"
  38. " popf""\n"
  39. " fld1""\n"
  40. " fldpi""\n"
  41. " fcomi %%st(1), %%st" "\n"
  42. " ffree %%st(0)" "\n"
  43. " ffree %%st(1)" "\n"
  44. " pushf""\n"
  45. " pop res_fcomi_1_pi""\n"
  46. " push %0""\n"
  47. " popf""\n"
  48. " fldpi""\n"
  49. " fld1""\n"
  50. " fcomi %%st(1), %%st" "\n"
  51. " ffree %%st(0)" "\n"
  52. " ffree %%st(1)" "\n"
  53. " pushf""\n"
  54. " pop res_fcomi_pi_1""\n"
  55. " push %0""\n"
  56. " popf""\n"
  57. " fld1""\n"
  58. " fld1""\n"
  59. " fcomi %%st(1), %%st" "\n"
  60. " ffree %%st(0)" "\n"
  61. " ffree %%st(1)" "\n"
  62. " pushf""\n"
  63. " pop res_fcomi_1_1""\n"
  64. :
  65. : "r" (flags)
  66. );
  67. if ((res_fcomi_1_pi & ARITH) != (0)) {
  68. printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
  69. return 1;
  70. }
  71. if ((res_fcomi_pi_1 & ARITH) != (CF)) {
  72. printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
  73. return 1;
  74. }
  75. if ((res_fcomi_1_1 & ARITH) != (ZF)) {
  76. printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
  77. return 1;
  78. }
  79. if (fetestexcept(FE_INVALID) != 0) {
  80. printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
  81. return 1;
  82. }
  83. return 0;
  84. }
  85. int test_qnan(long flags)
  86. {
  87. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  88. asm ("\n"
  89. " push %0""\n"
  90. " popf""\n"
  91. " flds qnan""\n"
  92. " fld1""\n"
  93. " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it
  94. " fcomi %%st(1), %%st" "\n"
  95. " ffree %%st(0)" "\n"
  96. " ffree %%st(1)" "\n"
  97. " pushf""\n"
  98. " pop res_fcomi_nan_1""\n"
  99. :
  100. : "r" (flags)
  101. );
  102. if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
  103. printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
  104. return 1;
  105. }
  106. if (fetestexcept(FE_INVALID) != FE_INVALID) {
  107. printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
  108. return 1;
  109. }
  110. return 0;
  111. }
  112. int testu_qnan(long flags)
  113. {
  114. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  115. asm ("\n"
  116. " push %0""\n"
  117. " popf""\n"
  118. " flds qnan""\n"
  119. " fld1""\n"
  120. " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it
  121. " fucomi %%st(1), %%st" "\n"
  122. " ffree %%st(0)" "\n"
  123. " ffree %%st(1)" "\n"
  124. " pushf""\n"
  125. " pop res_fcomi_nan_1""\n"
  126. :
  127. : "r" (flags)
  128. );
  129. if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
  130. printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
  131. return 1;
  132. }
  133. if (fetestexcept(FE_INVALID) != 0) {
  134. printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
  135. return 1;
  136. }
  137. return 0;
  138. }
  139. int testu_snan(long flags)
  140. {
  141. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  142. asm ("\n"
  143. " push %0""\n"
  144. " popf""\n"
  145. // " flds snan""\n" // WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register!
  146. // " fstpt snan1""\n" // if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111!
  147. // " fnclex""\n" // flds of a snan raised FE_INVALID, clear it
  148. " fldt snan80""\n" // fldt never raise FE_INVALID
  149. " fld1""\n"
  150. " fucomi %%st(1), %%st" "\n"
  151. " ffree %%st(0)" "\n"
  152. " ffree %%st(1)" "\n"
  153. " pushf""\n"
  154. " pop res_fcomi_nan_1""\n"
  155. :
  156. : "r" (flags)
  157. );
  158. if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
  159. printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
  160. return 1;
  161. }
  162. // printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]);
  163. if (fetestexcept(FE_INVALID) != FE_INVALID) {
  164. printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
  165. return 1;
  166. }
  167. return 0;
  168. }
  169. int testp(long flags)
  170. {
  171. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  172. asm ("\n"
  173. " push %0""\n"
  174. " popf""\n"
  175. " fld1""\n"
  176. " fldpi""\n"
  177. " fcomip %%st(1), %%st" "\n"
  178. " ffree %%st(0)" "\n"
  179. " pushf""\n"
  180. " pop res_fcomi_1_pi""\n"
  181. " push %0""\n"
  182. " popf""\n"
  183. " fldpi""\n"
  184. " fld1""\n"
  185. " fcomip %%st(1), %%st" "\n"
  186. " ffree %%st(0)" "\n"
  187. " pushf""\n"
  188. " pop res_fcomi_pi_1""\n"
  189. " push %0""\n"
  190. " popf""\n"
  191. " fld1""\n"
  192. " fld1""\n"
  193. " fcomip %%st(1), %%st" "\n"
  194. " ffree %%st(0)" "\n"
  195. " pushf""\n"
  196. " pop res_fcomi_1_1""\n"
  197. :
  198. : "r" (flags)
  199. );
  200. if ((res_fcomi_1_pi & ARITH) != (0)) {
  201. printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
  202. return 1;
  203. }
  204. if ((res_fcomi_pi_1 & ARITH) != (CF)) {
  205. printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
  206. return 1;
  207. }
  208. if ((res_fcomi_1_1 & ARITH) != (ZF)) {
  209. printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
  210. return 1;
  211. }
  212. if (fetestexcept(FE_INVALID) != 0) {
  213. printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
  214. return 1;
  215. }
  216. return 0;
  217. }
  218. int testp_qnan(long flags)
  219. {
  220. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  221. asm ("\n"
  222. " push %0""\n"
  223. " popf""\n"
  224. " flds qnan""\n"
  225. " fld1""\n"
  226. " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it
  227. " fcomip %%st(1), %%st" "\n"
  228. " ffree %%st(0)" "\n"
  229. " pushf""\n"
  230. " pop res_fcomi_nan_1""\n"
  231. :
  232. : "r" (flags)
  233. );
  234. if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
  235. printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
  236. return 1;
  237. }
  238. if (fetestexcept(FE_INVALID) != FE_INVALID) {
  239. printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
  240. return 1;
  241. }
  242. return 0;
  243. }
  244. int testup_qnan(long flags)
  245. {
  246. feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
  247. asm ("\n"
  248. " push %0""\n"
  249. " popf""\n"
  250. " flds qnan""\n"
  251. " fld1""\n"
  252. " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it
  253. " fucomip %%st(1), %%st" "\n"
  254. " ffree %%st(0)" "\n"
  255. " pushf""\n"
  256. " pop res_fcomi_nan_1""\n"
  257. :
  258. : "r" (flags)
  259. );
  260. if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
  261. printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
  262. return 1;
  263. }
  264. if (fetestexcept(FE_INVALID) != 0) {
  265. printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
  266. return 1;
  267. }
  268. return 0;
  269. }
  270. void sighandler(int sig)
  271. {
  272. printf("[FAIL]\tGot signal %d, exiting\n", sig);
  273. exit(1);
  274. }
  275. int main(int argc, char **argv, char **envp)
  276. {
  277. int err = 0;
  278. /* SIGILL triggers on 32-bit kernels w/o fcomi emulation
  279. * when run with "no387 nofxsr". Other signals are caught
  280. * just in case.
  281. */
  282. signal(SIGILL, sighandler);
  283. signal(SIGFPE, sighandler);
  284. signal(SIGSEGV, sighandler);
  285. printf("[RUN]\tTesting f[u]comi[p] instructions\n");
  286. err |= test(0);
  287. err |= test_qnan(0);
  288. err |= testu_qnan(0);
  289. err |= testu_snan(0);
  290. err |= test(CF|ZF|PF);
  291. err |= test_qnan(CF|ZF|PF);
  292. err |= testu_qnan(CF|ZF|PF);
  293. err |= testu_snan(CF|ZF|PF);
  294. err |= testp(0);
  295. err |= testp_qnan(0);
  296. err |= testup_qnan(0);
  297. err |= testp(CF|ZF|PF);
  298. err |= testp_qnan(CF|ZF|PF);
  299. err |= testup_qnan(CF|ZF|PF);
  300. if (!err)
  301. printf("[OK]\tf[u]comi[p]\n");
  302. else
  303. printf("[FAIL]\tf[u]comi[p] errors: %d\n", err);
  304. return err;
  305. }