; $Id: ftol2-vcc.asm 106510 2024-10-20 00:47:58Z vboxsync $ ;; @file ; IPRT - Floating Point to Integer related Visual C++ support routines. ; ; ; Copyright (C) 2022-2024 Oracle and/or its affiliates. ; ; This file is part of VirtualBox base platform packages, as ; available from https://www.virtualbox.org. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation, in version 3 of the ; License. ; ; This program is distributed in the hope that it will be useful, but ; WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ; General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, see . ; ; The contents of this file may alternatively be used under the terms ; of the Common Development and Distribution License Version 1.0 ; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included ; in the VirtualBox 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. ; ; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 ; %include "iprt/asmdefs.mac" %include "iprt/x86.mac" %define RTFLOAT80U_EXP_BIAS (16383) ;; ; Convert st0 to integer returning it in eax, popping st0. ; ; @returns value in eax ; @param st0 The value to convert. Will be popped. ; @uses eax, st0, FSW.TOP, FSW.exception ; GLOBALNAME_RAW __ftol2_sse_excpt, function, RT_NOTHING GLOBALNAME_RAW __ftol2_sse, function, RT_NOTHING ;; @todo kind of expect __ftol2_sse to take input in xmm0 and return in edx:eax. BEGINPROC_RAW __ftoi2 push ebp mov ebp, esp sub esp, 8h fisttp dword [esp] mov eax, [esp] leave ret ENDPROC_RAW __ftoi2 ;; ; Convert st0 to integer returning it in edx:eax, popping st0. ; ; @returns value in edx:eax ; @param st0 The value to convert. Will be popped. ; @uses eax, edx, st0, FSW.TOP, FSW.exception ; BEGINPROC_RAW __ftol2 push ebp mov ebp, esp sub esp, 8h and esp, 0fffffff8h ; proper alignment. fisttp qword [esp] mov eax, [esp] mov edx, [esp + 4] leave ret ENDPROC_RAW __ftol2 ;; ; Convert st0 to an unsigned integer returning it in edx:eax, popping st0. ; ; Used when /fpcvt:IA is given together with /ARCH:IA32 or /ARCH:SSE. ; ; @returns value in edx:eax ; @param st0 The value to convert. Will be popped. ; @uses eax, edx, st0, FSW.TOP ; BEGINPROC_RAW __ftoui2 push ebp mov ebp, esp ; Proper stack alignment for a qword store (see note below). sub esp, 8h and esp, 0fffffff8h ; proper alignment. ; Check if the value is unordered or negative. fldz fucomip st0, st1 jp .unordered ; PF=1 only when unordered. ja .negative ; jumps if ZF=0 & CF=0, i.e. if zero > value. ; Check if the value is too large for uint32_t. fld dword [g_r32TwoToThePowerOf32] fucomip st0, st1 ; if 1*2^32 <= value jbe .too_large ; then jmp ; ; The value is unproblematic, so just convert and return it. ; ; Note! We do a 64-bit conversion here, not a 32-bit. This helps with ; values at or above 2**31. ; fisttp qword [esp] ; Raise exceptions as appropriate, pop ST0. mov eax, [esp] .return: leave ret ; ; Negative value. ; .negative: ; If the value is -1.0 or smaller, treat it as unordered. fabs fld1 fucomip st0, st1 ; if 1.0 <= abs(value) jbe .unordered ; then jmp (jbe = jmp if ZF or/and CF set) ; Values between -1.0 and 0 (both exclusively) are truncated to zero. fisttp dword [esp] ; Raise exceptions as appropriate, pop ST0. xor eax, eax jmp .return ; Return MAX after maybe raising an exception. .unordered: .too_large: fcomp dword [g_r32QNaN] ; Set C0-3, raise exceptions as appropriate, pop ST0. mov eax, -1 jmp .return ENDPROC_RAW __ftoui2 ;; ; Convert st0 to an unsigned long integer returning it in edx:eax, popping st0. ; ; Used when /fpcvt:IA is given together with /ARCH:IA32 or /ARCH:SSE. ; ; @returns value in edx:eax ; @param st0 The value to convert. Will be popped. ; @uses eax, edx, st0, FSW.TOP ; BEGINPROC_RAW __ftoul2 push ebp mov ebp, esp ; We may need to store a RTFLOAT80 and uint64_t, so make sure the stack is 16 byte aligned. sub esp, 16h and esp, 0fffffff0h ; Check if the value is unordered or negative. fldz fucomip st0, st1 jp .unordered ; PF=1 only when unordered. ja .negative ; jumps if ZF=0 & CF=0, i.e. if zero > value. ; Check if the value is in signed 64-bit integer range, i.e. less than 1.0*2^63. fld dword [g_r32TwoToThePowerOf63] fucomip st0, st1 ; if 1*2^63 <= value jbe .larger_than_or_equal_to_2_to_the_power_of_63 ; then jmp; ; ; The value is unproblematic for 64-bit signed conversion, so just convert and return it. ; fisttp qword [esp] ; Raise exceptions as appropriate, pop ST0. mov edx, [esp + 4] mov eax, [esp] .return: leave ret ; ; We've got a value that so large that fisttp can't handle it, however ; it may still be covertable to uint64_t, iff the exponent is 63. ; .larger_than_or_equal_to_2_to_the_power_of_63: ; Save the value on the stack so we can examine it in it's full 80-bit format. fld st0 fstp tword [esp] ; RTFLOAT80U movzx eax, word [esp + 8] ; The exponent and (zero) sign value. %ifdef RT_STRICT ; Negative numbers shall not end up here, we checked for that in the zero compare above! bt eax, 15 jnc .sign_clear_as_expected int3 .sign_clear_as_expected: ; The exponent shall not be less than 63, we checked for that in the g_r32TwoToThePowerOf63 compare above! cmp eax, RTFLOAT80U_EXP_BIAS+63 jae .exp_not_below_63_as_expected int3 .exp_not_below_63_as_expected: %endif ; Check that the exponent is 63, because if it isn't it must be higher ; and out of range for a conversion to uint64_t. cmp eax, RTFLOAT80U_EXP_BIAS+63 jne .too_large ; Check for unnormal values just to be on the safe side. mov edx, [esp + 4] bt edx, 31 ; Check if the most significant bit in the mantissa is zero. jnc .unnormal ; Load the rest of the value. mov eax, [esp] frndint ; Clear C1 & raising exceptions as appropriate. ffreep st0 jmp .return ; ; Negative value. ; .negative: ; If the value is -1.0 or smaller, treat it as unordered. fabs fld1 fucomip st0, st1 ; if 1.0 <= abs(value) jbe .unordered ; then jmp (jbe = jmp if ZF or/and CF set) ; Values between -1.0 and 0 (both exclusively) are truncated to zero. fisttp qword [esp] ; Raise exceptions as appropriate, pop ST0. xor edx, edx xor eax, eax jmp .return ; ; Unordered or a value in the (-1.0, 0) range. ; Return -1 after popping ST0. ; .unordered: .unnormal: .too_large: fcomp dword [g_r32QNaN] ; Set C0-3, raise exceptions as appropriate, pop ST0. mov edx, -1 mov eax, -1 jmp .return ENDPROC_RAW __ftoul2 ;; ; Convert st0 to unsigned integer returning it in edx:eax, popping st0. ; ; This is new with VC 14.3 / 2022 and changes how extreme values are handled. ; ; This is used for /ARCH:IA32 & /ARCH:SSE, the compiler generates inline conversion ; code for /ARCH:SSE2 it seems. If either of those two are used with /fpcvt:IA, the ; __ftoul2 is used instead. ; ; @returns value in edx:eax ; @param st0 The value to convert. Will be popped. ; @uses eax, edx, st0, FSW.TOP ; BEGINPROC_RAW __ftoul2_legacy sub esp, 4 fst dword [esp] pop edx cmp edx, 0x5f800000 ; RTFLOAT32U jae __ftol2 ; Jump if exponent >= 64 or the value is signed. jmp __ftoul2 ENDPROC_RAW __ftoul2_legacy ; ; Constants. ; ALIGNCODE(4) g_r32TwoToThePowerOf32: dd 0x4f800000 ; 1.0*2^32 g_r32TwoToThePowerOf63: dd 0x5f000000 ; 1.0*2^63 g_r32QNaN: dd 0xffc00000 ; Quite negative NaN (RTFLOAT32U_INIT_QNAN)