;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code. ;;; ; basic_d.s ; ; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. ; ; RCS Revision: 1 ; Checkin Date: 2007/06/29 02:59:16 ; Revising Author ; Basic floating point functions ; ; ; Revisions: ; Fixed == and != compares to be IEEE-754 compliant when input QNaNs. ; No exceptions are raised when only QNaNs are the only NaNs input to ; == and !=. Moved NaN detection and exception raising here. ; Removed unnecessary macros for compares that return results in flags. ; Added WindowsCE SEH mechanism support. ; Renamed routines. ; ; Local storage size and offsets LOC_SIZE EQU 0x20 OrgOp2h EQU 0x1C OrgOp2l EQU 0x18 OrgOp1h EQU 0x14 OrgOp1l EQU 0x10 ExDResl EQU 0x08 ExOp2h EQU 0x04 ExOp2l EQU 0x00 NewResl EQU 0x10 GET fpe.asm GET kxarm.inc ;============================================================================== ; Compare ; ; BUGBUG: This documentation is not completely correct. For == and != ; comparisions, only SNANs can raise the invalid operation ; exception. For all other compares, both SNANs and QNANs ; can raise the invalid operation exception and return FALSE ; (they actually compare unordered). When == compares unordered ; (contains 1 or more NANs) it also returns FALSE. When != ; compares unordered, it returns TRUE. See IEEE-754-1985 for ; details. The described behavior is implemented here. ; ; ; ; This isn't as simple as it could be. The problem is that NaNs may cause an ; exception and always compare as FALSE if not signalling. Infinities need to ; be treated as normal numbers, although they look like NaNs. ; Furthermore +0 = -0 needs a special check. ; ; General comparison instruction flow: (this is less than) ; ; OP1 < 0 OR OP2 < 0 ; | ; +--------Y--------------+------------N-------------+ ; | | ; (OP1 OR OP2) NaN? (OP1 OR OP2) NaN? ; | | ; +----N---+---Y------+ +-----Y-------+----N-----+ ; | | | | ; RET OP1 < OP2 OP1 or OP2 inf/NaN? OP1 or OP2 inf/NaN? RET OP1 > OP2 ; | | AND NOT ; +---N--+---Y--+ +---Y--+--N----+ (OP1 = 0 AND OP2 = 0) ; | | | | ; RET OP1 < OP2 (OP1 NaN?) OR (OP2 NaN?) RET OP1 > OP2 ; | | | ; | +--N--+--Y--> exception | ; | | | ; | OP1 < 0 OR OP2 < 0? | ; | | | ; +-----N-------+------------Y-----------+ ; ; The first layer selects between the case where both operands are positive or ; when at least one is negative. The second layer uses a quick test on the ; operands orred together to determine whether they look like a NaN. This check is ; weak: it will get about 4% or 9% 'false hits' for doubles and floats, where ; none of the operands is a NaN. In general false hits occur for very large numbers, ; or for both numbers around 2.0 (one larger, one smaller). ; If the operands are not categorized a NaNs, a normal unsigned compare does the ; actual work. It returns immediately if the highwords of the operands are different. ; Note that the negative case uses a compare with the operands swapped, ; as the order is reversed for negative numbers. The negative case also checks for ; -0 == 0 as a special case. In the NaN code, a more precise check is done, which ; filters out NaNs and infinities, and the normal compare follows otherwise. ; The exception handler raises a Invalid Operation exception if one of the operands ; is a NaN (ignoring the signal bit). ; There are thus 3 different checks on NaNs, with increasing accuracy: ; 1. one of the operands looks like a NaN (but might not be one). ; 2. one of the operands is infinite or NaN. ; 3. one of the operands is a NaN. ; ; The compare routine can either be used as a boolean returning function (dgt, ; dge, dlt, dle) or as a flags returning function (returning < as LO, <= as LS, ; > as HI, >= as HS). ; ; The routine is optimised for the both operands positive which not look like ; NaNs case. It is also assumed the chance that the highwords of the operands are ; equal is less than 50%. Timing: ; Flags: 7/9 (pos), 11/13 (false NaN), 10/12 (neg), 13/15 (false NaN) SA1.1 cycles. ; EQ/NE/HI/HS/LO/LS: 10 / 14 / 13 / 16 ;============================================================================== MACRO CmpReturn $cc MOV a1, #0 MOV$cc a1, #1 ADD sp, sp, #LOC_SIZE IF Interworking :LOR: Thumbing LDMFD sp!, {lr} BX lr ELSE LDMFD sp!, {pc} ENDIF MEND MACRO $lab DoubleCompare $cc, $NaN_lab ASSERT "$cc"="LO":LOR:"$cc"="LS":LOR:"$cc"="HS":LOR:"$cc"="HI":LOR:"$cc"="EQ":LOR:"$cc"="NE" NESTED_ENTRY $lab EnterWithLR_16 STMFD sp!, {lr} ; Save return address SUB sp, sp, #LOC_SIZE ; Allocate local storage PROLOG_END ORRS tmp, dOP1h, dOP2h BMI $lab._negative ; one of the operands negative? (MI) CMN tmp, #0x00100000 ; check whether operands might be infinite/NaN BMI $lab._check_NaN_pos CMP dOP1h, dOP2h CMPEQ dOP1l, dOP2l CmpReturn $cc $lab._check_NaN_pos ; opnd1/2 might be inf/NaNs - do more accurate check CMN dOP1h, #0x00100000 ; overhead 4 cycles for false hit CMNPL dOP2h, #0x00100000 BMI $lab._Inf_or_NaN $lab._cmp_pos CMP dOP1h, dOP2h CMPEQ dOP1l, dOP2l CmpReturn $cc $lab._negative CMN tmp, #0x00100000 BPL $lab._check_NaN_neg ; check whether operands might be infinite/NaN ORRS tmp, dOP1l, dOP1h, LSL #1 ; check for -0 == 0 ORREQS tmp, dOP2l, dOP2h, LSL #1 CMPNE dOP2h, dOP1h CMPEQ dOP2l, dOP1l CmpReturn $cc $lab._check_NaN_neg ; opnd1/2 might be inf/NaNs - do more accurate check MOV tmp, #0x00200000 ; overhead 3 cycles for false hit CMN tmp, dOP1h, LSL #1 CMNCC tmp, dOP2h, LSL #1 BCS $lab._Inf_or_NaN $lab._cmp_neg ; -0 == 0 test omitted (cannot give a false hit) CMP dOP2h, dOP1h CMPEQ dOP2l, dOP1l CmpReturn $cc $lab._Inf_or_NaN ; one of the operands is infinite or NaN MOV tmp, #0x00200000 CMN tmp, dOP1h, LSL #1 CMPEQ dOP1l, #0 ; HI -> NaN found CMNLS tmp, dOP2h, LSL #1 ; no NaN, check opnd2 CMPEQ dOP2l, #0 BHI $NaN_lab ; NaN found -> exception ORRS tmp, dOP1h, dOP2h BPL $lab._cmp_pos B $lab._cmp_neg MEND ;============================================================================== ;Invalid Operation checking (NaNs on compares) ;; IMPORT FPE_Raise ;; ;; NANs on compares <, >, <=, and >= ;; ;; SNANs and QNANs both raise the invalid operation exception, so we don't ;; care which kind of NAN we get. This is because if we get an SNAN or SNANs, ;; we raise the invalid operation exception. If we get a QNAN or QNANs, we ;; have an unordered compare and must also raise the invalid operation ;; exception. ;; ;; Register usage on entry: ;; r0 - Arg1.low ;; r1 - Arg1.high ;; r2 - Arg2.low ;; r3 - Arg2.high ;; r14 - available for scratch ;; All others have normal usage semantics. ;; MACRO $l DCmpNaN $Filter_lab $l STR r2, [sp, #ExOp2l] ;; Push Arg2.low STR r3, [sp, #ExOp2h] ;; Push Arg2.high MOV r3, #_FpCompareUnordered ;; Load default result STR r3, [sp, #ExDResl] ;; Push default result MOV r3, r1 ;; Arg1.high MOV r2, r0 ;; Arg1.low MOV r1, #_FpCmpD ;; ExInfo: InvalidOp, double compare ORR r1, r1, #IVO_bit ;; .. ADD r0, sp, #NewResl ;; Pointer to result CALL FPE_Raise ;; Deal with exception information IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #NewResl] ;; Load return value ADD sp, sp, #LOC_SIZE ;; Restore stack ;; ;; Register usage: ;; ;; r0 - Result from exception handler ;; ;; We must now examine the result from the exception handler and change it ;; to TRUE or FALSE, depending on the operation. After changing the result, ;; we return to the caller of the FP double compare routine. ;; B $Filter_lab MEND ;; ;; NANs on compares == and != ;; ;; SNANs and QNANs are treated differently for == and !=. If we get an SNAN ;; or SNANs, we must raise the invalid operation exception. If we only have ;; a QNAN or QNANs, then we simply return false and true for == and !=, ;; respectively. Unordered comparisions for == and != do not raise the ;; invalid operation exception. ;; ;; Register usage on entry: ;; r0 - Arg1.low ;; r1 - Arg1.high ;; r2 - Arg2.low ;; r3 - Arg2.high ;; r14 - available for scratch ;; All others have normal usage semantics. ;; MACRO $l DCmpSNaN $Filter_lab $l MOV r12, #0x7F0 ;; r12 = Max exponent = 0x7FF ORR r12, r12, #0x00F ;; ... MOV r14, r1, LSL #1 ;; Extract exponent from Arg1 MOV r14, r14, LSR #21 ;; ... CMP r14, r12 ;; Arg1.exponent == 0x7FF? BNE $l.checkArg2 ;; Arg1 not a NaN so check Arg2 MOV r14, r1, LSL #14 ;; r14 = Arg1.Mantissa.High ORRS r14, r14, r0 ;; Any Arg1.Mantissa bits set? BEQ $l.checkArg2 ;; Arg1 not a NaN so check Arg2 TST r1, #dSignalBit ;; Check if SNAN BEQ $l.SNaN ;; If high mant. bit clear, SNaN $l.checkArg2 MOV r14, r3, LSL #1 ;; Extract exponent from Arg2 MOV r14, r14, LSR #21 ;; ... CMP r14, r12 ;; Arg2.exponent == 0x7FF? BNE $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN MOV r14, r3, LSL #12 ;; r14 = Arg2.Mantissa.High ORRS r14, r14, r2 ;; Any Arg2.Mantissa bits set? BEQ $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN TST r3, #dSignalBit ;; Check if SNAN BEQ $l.SNaN ;; If high mant. bit clear, SNaN $l.cmpUnordered MOV r0, #_FpCompareUnordered ;; Have an unordered compare so B $Filter_lab ;; don't raise an exception $l.SNaN STR r2, [sp, #ExOp2l] ;; Push Arg2.low STR r3, [sp, #ExOp2h] ;; Push Arg2.high MOV r3, #_FpCompareUnordered ;; Load default result STR r3, [sp, #ExDResl] ;; Push default result MOV r3, r1 ;; Arg1.high MOV r2, r0 ;; Arg1.low MOV r1, #_FpCmpD ;; ExInfo: InvalidOp, double compare ORR r1, r1, #IVO_bit ;; .. ADD r0, sp, #NewResl ;; Pointer to result CALL FPE_Raise ;; Deal with exception information IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #NewResl] ;; Load return value ;; ;; Register usage: ;; ;; r0 - Result from exception handler ;; ;; We must now examine the result from the exception handler and change it ;; to TRUE or FALSE, depending on the operation. After changing the result, ;; we return to the caller of the FP double compare routine. ;; B $Filter_lab MEND ;============================================================================== ;Equality [ :DEF: eq_s Export __eqd AREA |.text|, CODE, READONLY __eqd DoubleCompare EQ, __eqd_NaN __eqd_NaN DCmpSNaN __eqd_Filter __eqd_Filter CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false ADD sp, sp, #LOC_SIZE ;; Restore stack IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __eqd ] ;============================================================================== ;Inequality [ :DEF: neq_s Export __ned AREA |.text|, CODE, READONLY __ned DoubleCompare NE, __ned_NaN __ned_NaN DCmpSNaN __ned_Filter __ned_Filter CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #0 ;; If did, return false MOVNE r0, #1 ;; else return true ADD sp, sp, #LOC_SIZE ;; Restore stack IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __ned ] ;============================================================================== ;Less Than [ :DEF: ls_s Export __ltd AREA |.text|, CODE, READONLY __ltd DoubleCompare LO, __ltd_NaN __ltd_NaN DCmpNaN __ltd_Filter __ltd_Filter CMP r0, #_FpCompareLess ;; Check if compared < MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __ltd ] ;============================================================================== ;Less Than or Equal [ :DEF: leq_s Export __led AREA |.text|, CODE, READONLY __led DoubleCompare LS, __led_NaN __led_NaN DCmpNaN __led_Filter __led_Filter CMP r0, #_FpCompareLess ;; Check if compared < MOVEQ r0, #1 ;; If did, BEQ __led_Filter_end ;; return true CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false __led_Filter_end IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __led ] ;============================================================================== ;Greater Than [ :DEF: gr_s Export __gtd AREA |.text|, CODE, READONLY __gtd DoubleCompare HI, __gtd_NaN __gtd_NaN DCmpNaN __gtd_Filter __gtd_Filter CMP r0, #_FpCompareGreater ;; Check if compared > MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __gtd ] ;============================================================================== ;Greater Than or Equal [ :DEF: geq_s Export __ged AREA |.text|, CODE, READONLY __ged DoubleCompare HS, __ged_NaN __ged_NaN DCmpNaN __ged_Filter __ged_Filter CMP r0, #_FpCompareGreater ;; Check if compared > MOVEQ r0, #1 ;; If did, BEQ __ged_Filter_end ;; return true CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false __ged_Filter_end IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __ged ] END