singrdk/base/Kernel/Native/arm/Crt/basic_d.asm

523 lines
18 KiB
NASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; 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