Index: /trunk/src/VBox/Runtime/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/Makefile.kmk	(revision 96278)
+++ /trunk/src/VBox/Runtime/Makefile.kmk	(revision 96279)
@@ -1937,4 +1937,6 @@
   	common/math/log.asm \
   	common/math/logf.asm \
+  	common/math/log2.asm \
+  	common/math/log2f.asm \
   	common/math/llrint.asm \
   	common/math/llrintf.asm \
@@ -2008,4 +2010,6 @@
   	common/math/log.asm \
   	common/math/logf.asm \
+  	common/math/log2.asm \
+  	common/math/log2f.asm \
   	common/math/llrint.asm \
   	common/math/llrintf.asm \
Index: /trunk/src/VBox/Runtime/common/math/log2f.asm
===================================================================
--- /trunk/src/VBox/Runtime/common/math/log2f.asm	(revision 96279)
+++ /trunk/src/VBox/Runtime/common/math/log2f.asm	(revision 96279)
@@ -0,0 +1,217 @@
+; $Id$
+;; @file
+; IPRT - No-CRT log2f - AMD64 & X86.
+;
+
+;
+; Copyright (C) 2006-2022 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+
+%define RT_ASM_WITH_SEH64
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+BEGINCODE
+
+extern NAME(RT_NOCRT(feraiseexcept))
+
+;;
+; Compute the log2f of rf
+; @returns st(0) / xmm0
+; @param    rf      [xSP + xCB*2] / xmm0
+RT_NOCRT_BEGINPROC log2f
+        push    xBP
+        SEH64_PUSH_xBP
+        mov     xBP, xSP
+        SEH64_SET_FRAME_xBP 0
+        sub     xSP, 20h
+        SEH64_ALLOCATE_STACK 20h
+        SEH64_END_PROLOGUE
+
+        ;
+        ; Load the input into st0.
+        ;
+%ifdef RT_ARCH_AMD64
+        movss   [xBP - 10h], xmm0
+        fld     dword [xBP - 10h]
+%else
+        fld     dword [xBP + xCB*2]
+%endif
+
+        ;
+        ; Weed out non-normal values.
+        ;
+        fxam
+        fnstsw  ax
+        mov     cx, ax
+        and     ax, X86_FSW_C3 | X86_FSW_C2 | X86_FSW_C0
+        cmp     ax, X86_FSW_C2              ; Normal finite number (excluding zero)
+        je      .finite
+        cmp     ax, X86_FSW_C3              ; Zero
+        je      .zero
+        cmp     ax, X86_FSW_C3 | X86_FSW_C2 ; Denormals
+        je      .finite
+        cmp     ax, X86_FSW_C0 | X86_FSW_C2 ; Infinity.
+        je      .inf
+        jmp     .nan
+
+.finite:
+        ; Negative number?
+        test    cx, X86_FSW_C1
+        jnz     .negative
+
+        ; Is it +1.0?
+        fld1
+        fcomip  st1
+        jz      .plus_one
+
+        ;
+        ; The fyl2xp1 instruction (ST1=ST1*log2(ST0+1.0), popping ST0) has a
+        ; valid ST0 range of 1(1-sqrt(0.5)) (approx 0.29289321881) on both
+        ; sides of zero.  We try use it if we can.
+        ;
+.above_one:
+        ; For both fyl2xp1 and fyl2xp1 we need st1=1.0.
+        fld1
+        fxch    st0, st1                    ; -> st0=input; st1=1.0
+
+        ; Check if the input is within the fyl2xp1 range.
+        fld     qword [.s_r64AbsFyL2xP1InputMax xWrtRIP]
+        fcomip  st0, st1
+        jbe     .cannot_use_fyl2xp1
+
+        fld     qword [.s_r64AbsFyL2xP1InputMin xWrtRIP]
+        fcomip  st0, st1
+        jae     .cannot_use_fyl2xp1
+
+        ; Do the calculation.
+.use_fyl2xp1:
+        fsub    st0, st1                    ; -> st0=input-1; st1=1.0
+        fyl2xp1                             ; -> st0=1.0*log2(st0+1.0)
+        jmp     .return_val
+
+.cannot_use_fyl2xp1:
+        fyl2x                               ; -> st0=1.0*log2(st0)
+
+        ;
+        ; Run st0.
+        ;
+.return_val:
+%ifdef RT_ARCH_AMD64
+        fstp    dword [xBP - 10h]
+        movss   xmm0, [xBP - 10h]
+%endif
+.return:
+        leave
+        ret
+
+
+        ;
+        ; +1.0: Return +0.0.
+        ;
+.plus_one:
+        ffreep  st0
+        fldz
+        jmp     .return_val
+
+        ;
+        ; Negative numbers: Return NaN and raise invalid operation.
+        ;
+.negative:
+.minus_inf:
+        ; Raise invalid operation
+%ifdef RT_ARCH_X86
+        mov     dword [xSP], X86_FSW_IE
+%elifdef ASM_CALL64_GCC
+        mov     edi, X86_FSW_IE
+%elifdef ASM_CALL64_MSC
+        mov     ecx, X86_FSW_IE
+%else
+ %error calling conv.
+%endif
+        call    NAME(RT_NOCRT(feraiseexcept))
+
+        ; Load NaN
+%ifdef RT_ARCH_AMD64
+        movss   xmm0, [.s_r32NaN xWrtRIP]
+%else
+        fld     dword [.s_r32NaN xWrtRIP]
+%endif
+        jmp     .return
+
+        ;
+        ; +/-0.0: Return inf and raise divide by zero error.
+        ;
+.zero:
+        ffreep  st0
+
+        ; Raise div/0
+%ifdef RT_ARCH_X86
+        mov     dword [xSP], X86_FSW_ZE
+%elifdef ASM_CALL64_GCC
+        mov     edi, X86_FSW_ZE
+%elifdef ASM_CALL64_MSC
+        mov     ecx, X86_FSW_ZE
+%else
+ %error calling conv.
+%endif
+        call    NAME(RT_NOCRT(feraiseexcept))
+
+        ; Load +Inf
+%ifdef RT_ARCH_AMD64
+        movss   xmm0, [.s_r32MinusInf xWrtRIP]
+%else
+        fld     dword [.s_r32MinusInf xWrtRIP]
+%endif
+        jmp     .return
+
+        ;
+        ; -Inf: Same as other negative numbers
+        ; +Inf: return +Inf.  Join path with NaN.
+        ;
+.inf:
+        test    cx, X86_FSW_C1              ; sign bit
+        jnz     .minus_inf
+
+        ;
+        ; NaN: Return the input NaN value as is, if we can.
+        ;
+.nan:
+%ifdef RT_ARCH_AMD64
+        ffreep  st0
+%endif
+        jmp     .return
+
+ALIGNCODE(8)
+        ;; The fyl2xp1 instruction only works between +/-1(1-sqrt(0.5)).
+        ; These two variables is that range + 1.0, so we can compare directly
+        ; with the input w/o any extra fsub and fabs work.
+.s_r64AbsFyL2xP1InputMin:
+        dq      0.708 ; -0.292 + 1.0
+.s_r64AbsFyL2xP1InputMax:
+        dq      1.292
+.s_r32MinusInf:
+        dd      RTFLOAT32U_INF_MINUS
+.s_r32NaN:
+        dd      RTFLOAT32U_QNAN_MINUS
+ENDPROC   RT_NOCRT(log2f)
+
Index: /trunk/src/VBox/Runtime/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/testcase/Makefile.kmk	(revision 96278)
+++ /trunk/src/VBox/Runtime/testcase/Makefile.kmk	(revision 96279)
@@ -672,4 +672,6 @@
 	../common/math/log.asm \
 	../common/math/logf.asm \
+	../common/math/log2.asm \
+	../common/math/log2f.asm \
 	../common/math/lrint.asm \
 	../common/math/lrintf.asm \
Index: /trunk/src/VBox/Runtime/testcase/tstRTNoCrt-2.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstRTNoCrt-2.cpp	(revision 96278)
+++ /trunk/src/VBox/Runtime/testcase/tstRTNoCrt-2.cpp	(revision 96279)
@@ -433,4 +433,16 @@
                          __LINE__, #a_Fn, #a_Args, g_szFloat[0], g_szFloat[1]); \
         } \
+    } while (0)
+
+
+
+#define CHECK_XCPT(a_InnerTestExpr, a_fXcptMask, a_fXcptExpect) do { \
+        RT_NOCRT(feclearexcept(RT_NOCRT_FE_ALL_EXCEPT)); \
+        a_InnerTestExpr; \
+        int const fXcpt = RT_NOCRT(fetestexcept)(RT_NOCRT_FE_ALL_EXCEPT); \
+        if ((fXcpt & (a_fXcptMask)) != (a_fXcptExpect)) \
+            RTTestFailed(g_hTest, "line %u: %s -^-> %#x, expected %#x (%s)", \
+                          __LINE__, #a_InnerTestExpr, fXcpt, (a_fXcptExpect), #a_fXcptExpect); \
+        RT_NOCRT(feclearexcept(RT_NOCRT_FE_ALL_EXCEPT)); \
     } while (0)
 
@@ -2704,4 +2716,71 @@
     CHECK_FLT_SAME(logf,(RTStrNanFloat("s",  true)));
     CHECK_FLT_SAME(logf,(RTStrNanFloat("s", false)));
+}
+
+
+void testLog2()
+{
+    RTTestSub(g_hTest, "log2[f]");
+
+    CHECK_DBL(           RT_NOCRT(log2)(RTStrNanDouble(NULL,    true)), RTStrNanDouble(NULL,    true));
+    CHECK_DBL(           RT_NOCRT(log2)(RTStrNanDouble("234",  false)), RTStrNanDouble("234",  false));
+    CHECK_DBL(           RT_NOCRT(log2)(RTStrNanDouble("999s", false)), RTStrNanDouble("999s", false));
+    CHECK_DBL(           RT_NOCRT(log2)(RTStrNanDouble("fffs",  true)), RTStrNanDouble("fffs",  true));
+    CHECK_XCPT(CHECK_DBL(RT_NOCRT(log2)(  +0.0), -INFINITY), RT_NOCRT_FE_DIVBYZERO, RT_NOCRT_FE_DIVBYZERO);
+    CHECK_XCPT(CHECK_DBL(RT_NOCRT(log2)(  -0.0), -INFINITY), RT_NOCRT_FE_DIVBYZERO, RT_NOCRT_FE_DIVBYZERO);
+    CHECK_XCPT(CHECK_DBL(RT_NOCRT(log2)(-123.0), RTStrNanDouble(NULL, false)), RT_NOCRT_FE_INVALID, RT_NOCRT_FE_INVALID);
+    CHECK_DBL(           RT_NOCRT(log2)(              1.0),   +0.0);
+    CHECK_DBL(           RT_NOCRT(log2)(              2.0),   +1.0);
+    CHECK_DBL(           RT_NOCRT(log2)(           1024.0),  +10.0);
+    CHECK_DBL(           RT_NOCRT(log2)(  1099511627776.0),  +40.0); /* _1T */
+    CHECK_DBL_SAME(               log2,(              1.0));
+    CHECK_DBL_SAME(               log2,(              2.0));
+    CHECK_DBL_SAME(               log2,(           1024.0));
+    CHECK_DBL_SAME(               log2,(  1099511627776.0)); /* _1T */
+    CHECK_DBL_SAME(               log2,(              1.5));
+    CHECK_DBL_SAME(               log2,(      1.234485e-5));
+    CHECK_DBL_SAME(               log2,(      1.234485e+9));
+    CHECK_DBL_SAME(               log2,(    1.234485e+253));
+    CHECK_DBL_SAME(               log2,(        +INFINITY));
+    CHECK_DBL_SAME(               log2,(        -INFINITY));
+    CHECK_DBL_SAME(               log2,(         +DBL_MAX));
+    CHECK_DBL_SAME(               log2,(         -DBL_MAX));
+    CHECK_DBL_SAME(               log2,(RTStrNanDouble(NULL, true)));
+    CHECK_DBL_SAME(               log2,(RTStrNanDouble(NULL, false)));
+#if 0 /* UCRT doesn't preserve signalling NaN */
+    CHECK_DBL_SAME(               log2,(RTStrNanDouble("s",  true)));
+    CHECK_DBL_SAME(               log2,(RTStrNanDouble("s", false)));
+#endif
+
+    CHECK_FLT(           RT_NOCRT(log2f)(RTStrNanFloat(NULL,    true)), RTStrNanFloat(NULL,    true));
+    CHECK_FLT(           RT_NOCRT(log2f)(RTStrNanFloat("234",  false)), RTStrNanFloat("234",  false));
+    CHECK_FLT(           RT_NOCRT(log2f)(RTStrNanFloat("999s", false)), RTStrNanFloat("999s", false));
+    CHECK_FLT(           RT_NOCRT(log2f)(RTStrNanFloat("fffs",  true)), RTStrNanFloat("fffs",  true));
+    CHECK_XCPT(CHECK_FLT(RT_NOCRT(log2f)(  +0.0f), -(float)INFINITY), RT_NOCRT_FE_DIVBYZERO, RT_NOCRT_FE_DIVBYZERO);
+    CHECK_XCPT(CHECK_FLT(RT_NOCRT(log2f)(  -0.0f), -(float)INFINITY), RT_NOCRT_FE_DIVBYZERO, RT_NOCRT_FE_DIVBYZERO);
+    CHECK_XCPT(CHECK_FLT(RT_NOCRT(log2f)(-123.0f), RTStrNanFloat(NULL, false)), RT_NOCRT_FE_INVALID, RT_NOCRT_FE_INVALID);
+    CHECK_FLT(           RT_NOCRT(log2f)(              1.0f),   +0.0f);
+    CHECK_FLT(           RT_NOCRT(log2f)(              2.0f),   +1.0f);
+    CHECK_FLT(           RT_NOCRT(log2f)(           1024.0f),  +10.0f);
+    CHECK_FLT(           RT_NOCRT(log2f)(  1099511627776.0f),  +40.0f); /* _1T */
+    CHECK_FLT_SAME(               log2f,(              1.0f));
+    CHECK_FLT_SAME(               log2f,(              2.0f));
+    CHECK_FLT_SAME(               log2f,(           1024.0f));
+    CHECK_FLT_SAME(               log2f,(  1099511627776.0f)); /* _1T */
+    CHECK_FLT_SAME(               log2f,(              1.5f));
+    CHECK_FLT_SAME(               log2f,(      1.234485e-5f));
+    CHECK_FLT_SAME(               log2f,(      1.234485e+9f));
+    CHECK_FLT_SAME(               log2f,(     1.234485e+35f));
+    CHECK_FLT_SAME_RELAXED_NAN(   log2f,(  +(float)INFINITY)); /* UCRT returns +QNaN here, but log2 reutrn -QNaN. */
+    CHECK_FLT_SAME_RELAXED_NAN(   log2f,(  -(float)INFINITY)); /* ditto */
+    CHECK_FLT_SAME(               log2f,(          +FLT_MAX));
+    CHECK_FLT_SAME_RELAXED_NAN(   log2f,(          -FLT_MAX)); /* UCRT returns +QNaN here, but log2 reutrn -QNaN. */
+    CHECK_FLT_SAME(               log2f,(RTStrNanFloat(NULL, true)));
+    CHECK_FLT_SAME(               log2f,(RTStrNanFloat(NULL, false)));
+#if 0 /* UCRT doesn't preserve signalling NaN */
+    CHECK_FLT_SAME(               log2f,(RTStrNanDouble("s",  true)));
+    CHECK_FLT_SAME(               log2f,(RTStrNanDouble("s", false)));
+#endif
+
 }
 
@@ -3397,4 +3476,5 @@
     testRemainder();
     testLog();
+    testLog2();
     testSqRt();
 
