523 lines
18 KiB
NASM
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
|