针对SSE2前处理器的Java运行时如何实现浮点基本操作?

当设置strictfp时,(没有)针对没有SSE2的英特尔处理器的Java运行时如何处理浮点非正规数?

即使将387 FPU设置为53位精度,它仍保持超大的指数范围:

  1. 强制检测每个中间结果的下溢/溢出,和
  2. 这使得很难避免非正规的双重舍入。

策略包括重新计算导致模拟浮点的非正规值的操作,或者沿着这种技术的线路的永久指数偏移,为OCaml配备63位浮点数,从指数借用一点以避免双重-四舍五入。

在任何情况下,除非可以静态地确定操作不下溢/溢出,否则我认为没有办法避免每个浮点计算至少有一个条件分支。 如何处理exception(溢出/下溢)情况是我的问题的一部分,但这不能与表示的问题分开(永久指数偏移策略似乎意味着只需要检查溢出)。

它从我看来,从一个非常简单的测试用例,就像JVM往返每次通过内存的double计算来获得它想要的舍入。 它似乎也用一些魔法常数做了一些奇怪的事情。 这就是它为我做一个简单的“计算2天真”程序:

 0xb1e444b0: fld1 0xb1e444b2: jmp 0xb1e444dd ;*iload ; - fptest::calc@9 (line 6) 0xb1e444b7: nop 0xb1e444b8: fldt 0xb523a2c8 ; {external_word} 0xb1e444be: fmulp %st,%st(1) 0xb1e444c0: fmull 0xb1e44490 ; {section_word} 0xb1e444c6: fldt 0xb523a2bc ; {external_word} 0xb1e444cc: fmulp %st,%st(1) 0xb1e444ce: fstpl 0x10(%esp) 0xb1e444d2: inc %esi ; OopMap{off=51} ;*goto ; - fptest::calc@22 (line 6) 0xb1e444d3: test %eax,0xb3f8d100 ; {poll} 0xb1e444d9: fldl 0x10(%esp) ;*goto ; - fptest::calc@22 (line 6) 0xb1e444dd: cmp %ecx,%esi 0xb1e444df: jl 0xb1e444b8 ;*if_icmpge ; - fptest::calc@12 (line 6) 

我相信来自热点源代码的0xb523a2c80xb523a2bc_fpu_subnormal_bias1_fpu_subnormal_bias2_fpu_subnormal_bias1看起来是0x03ff8000000000000000_fpu_subnormal_bias2看起来是0x7bff8000000000000000_fpu_subnormal_bias1具有将最小正常double缩放到最小正常long double ; 如果FPU舍入到53位,那么“正确的事情”就会发生。

我推测看似毫无意义的test指令就在那里,以便在需要GC的情况下标记该页面是不可读的,从而可以中断线程。

这是Java代码:

 import java.io.*; public strictfp class fptest { public static double calc(int k) { double a = 2.0; double b = 1.0; for (int i = 0; i < k; i++) { b *= a; } return b; } public static double intest() { double d = 0; for (int i = 0; i < 4100; i++) d += calc(i); return d; } public static void main(String[] args) throws Exception { for (int i = 0; i < 100; i++) System.out.println(intest()); } } 

进一步深入研究,这些操作的代码在hotspot/src/cpu/x86/vm/x86_63.ad中的OpenJDK代码中hotspot/src/cpu/x86/vm/x86_63.ad 。 相关摘要:

 instruct strictfp_mulD_reg(regDPR1 dst, regnotDPR1 src) %{ predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current() ->method()->is_strict() ); match(Set dst (MulD dst src)); ins_cost(1); // Select this instruction for all strict FP double multiplies format %{ "FLD StubRoutines::_fpu_subnormal_bias1\n\t" "DMULp $dst,ST\n\t" "FLD $src\n\t" "DMULp $dst,ST\n\t" "FLD StubRoutines::_fpu_subnormal_bias2\n\t" "DMULp $dst,ST\n\t" %} opcode(0xDE, 0x1); /* DE C8+i or DE /1*/ ins_encode( strictfp_bias1(dst), Push_Reg_D(src), OpcP, RegOpc(dst), strictfp_bias2(dst) ); ins_pipe( fpu_reg_reg ); %} instruct strictfp_divD_reg(regDPR1 dst, regnotDPR1 src) %{ predicate (UseSSE<=1); match(Set dst (DivD dst src)); predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current() ->method()->is_strict() ); ins_cost(01); format %{ "FLD StubRoutines::_fpu_subnormal_bias1\n\t" "DMULp $dst,ST\n\t" "FLD $src\n\t" "FDIVp $dst,ST\n\t" "FLD StubRoutines::_fpu_subnormal_bias2\n\t" "DMULp $dst,ST\n\t" %} opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ ins_encode( strictfp_bias1(dst), Push_Reg_D(src), OpcP, RegOpc(dst), strictfp_bias2(dst) ); ins_pipe( fpu_reg_reg ); %} 

我没有看到加法和减法,但我敢打赌他们只是在53位模式下对FPU进行加/减,然后通过内存往返往返。 我有点好奇是否有一个棘手的溢出案例他们出错了,但我并不好奇地发现。