;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code. ;;; ; basic_f.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 ; ; 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. ; LOC_SIZE EQU 0x18 OrgOp1l EQU 0x14 OrgOp2l EQU 0x10 ExDResl EQU 0x08 ExOp2l EQU 0x00 NewResl EQU 0x10 GET fpe.asm GET kxarm.inc ;This code is very similar to the "double" versions. The documentation isn't ;extensively repeated. Refer to basic_d.s for further documentation. ;============================================================================== ; Compare. ; ; Timing: ; Flags: 7 (pos), 11 (false NaN), 9 (neg), 13 (false NaN) SA1.1 cycles ; Others: 9 / 13 / 11 / 15 ;============================================================================== 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 FloatCompare $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, fOP1, fOP2 ; separate into opnd1/2 both positive, or one negative BMI $lab._negative CMN tmp, #1 << 23 ; check whether operands might be infinite/NaN BMI $lab._NaN_check_pos CMP fOP1, fOP2 CmpReturn $cc $lab._NaN_check_pos ; opnd1/2 might be inf/NaNs - now do the real check CMN fOP1, #1 << 23 ; these get about 9% false hits - overhead 4 cycles CMNPL fOP2, #1 << 23 BMI $lab._Inf_or_NaN_pos $lab._cmp_pos CMP fOP1, fOP2 CmpReturn $cc $lab._Inf_or_NaN_pos ; at least one operand infinite or NaN - filter out infinities MOV tmp, #1 << 24 CMN tmp, fOP1, LSL #1 CMNLS tmp, fOP2, LSL #1 BLS $lab._cmp_pos ; no NaN - continue compare B $NaN_lab $lab._negative ; at least one negative operand CMN tmp, #1 << 23 BPL $lab._NaN_check_neg MOVS tmp, tmp, LSL #1 ; check -0 == 0 (CS & EQ -> HS and LS) CMPNE fOP2, fOP1 CmpReturn $cc $lab._NaN_check_neg ; opnd1/2 might be inf/NaNs - now do the real check MOV tmp, #1 << 24 ; these get about 9% false hits - overhead 4 cycles CMN tmp, fOP1, LSL #1 CMNLS tmp, fOP2, LSL #1 BHI $NaN_lab CMP fOP2, fOP1 ; -0 == 0 check not needed... CmpReturn $cc 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 ;; r1 - Arg2 ;; r14 - available for scratch ;; All others have normal usage semantics. ;; MACRO $l FCmpNaN $Filter_lab $l STR r1, [sp, #ExOp2l] ;; Push Arg2 MOV r3, #_FpCompareUnordered ;; Load default result STR r3, [sp, #ExDResl] ;; Push default result MOV r2, r0 ;; Arg1 MOV r1, #IVO_bit ;; ExInfo: InvalidOp, ORR r1, r1, #_FpCmpS ;; float compare 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 ;; Retore 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 ;; r1 - Arg2 ;; r14 - available for scratch ;; All others have normal usage semantics. ;; MACRO $l FCmpSNaN $Filter_lab $l MOV r14, r0, LSL #1 ;; Extract exponent from Arg1 MOV r14, r14, LSR #24 ;; ... CMP r14, #0xFF ;; Arg1.exponent == 0xFF? BNE $l.checkArg2 ;; Arg1 not a NaN so check Arg2 MOVS r14, r0, LSL #9 ;; Arg1.Mantissa bits set? BEQ $l.checkArg2 ;; Arg1 not a NaN so check Arg2 TST r0, #fSignalBit ;; Check if SNAN BEQ $l.SNaN ;; If high mant. bit clear, SNaN $l.checkArg2 MOV r14, r1, LSL #1 ;; Extract exponent from Arg2 MOV r14, r14, LSR #24 ;; ... CMP r14, #0xFF ;; Arg2.exponent == 0xFF? BNE $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN MOVS r14, r1, LSL #9 ;; Arg2.Mantissa bits set? BEQ $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN TST r1, #fSignalBit ;; 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 r1, [sp, #ExOp2l] ;; Push Arg2 MOV r3, #_FpCompareUnordered ;; Load default result STR r3, [sp, #ExDResl] ;; Push default result MOV r2, r0 ;; Arg1 MOV r1, #IVO_bit ;; ExInfo: InvalidOp, ORR r1, r1, #_FpCmpS ;; float compare 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 __eqs AREA |.text|, CODE, READONLY __eqs FloatCompare EQ, __eqs_NaN __eqs_NaN FCmpSNaN __eqs_Filter __eqs_Filter CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false ADD sp, sp, #0x18 ;; Pop extra arg passing space IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __eqs ] ;============================================================================== ;Inequality [ :DEF: neq_s Export __nes AREA |.text|, CODE, READONLY __nes FloatCompare NE, __nes_NaN __nes_NaN FCmpSNaN __nes_Filter __nes_Filter CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #0 ;; If did, return false MOVNE r0, #1 ;; else return true ADD sp, sp, #0x18 ;; Pop extra arg passing space IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __nes ] ;============================================================================== ;Less Than [ :DEF: ls_s Export __lts AREA |.text|, CODE, READONLY __lts FloatCompare LO, __lts_NaN __lts_NaN FCmpNaN __lts_Filter __lts_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 __lts ] ;============================================================================== ;Less Than or Equal [ :DEF: leq_s Export __les AREA |.text|, CODE, READONLY __les FloatCompare LS, __les_NaN __les_NaN FCmpNaN __les_Filter __les_Filter CMP r0, #_FpCompareLess ;; Check if compared < MOVEQ r0, #1 ;; If did, BEQ __les_Filter_end ;; return true CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false __les_Filter_end IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __les ] ;============================================================================== ;Greater Than [ :DEF: gr_s Export __gts AREA |.text|, CODE, READONLY __gts FloatCompare HI, __gts_NaN __gts_NaN FCmpNaN __gts_Filter __gts_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 __gts ] ;============================================================================== ;Greater Than or Equal [ :DEF: geq_s Export __ges AREA |.text|, CODE, READONLY __ges FloatCompare HS, __ges_NaN __ges_NaN FCmpNaN __ges_Filter __ges_Filter CMP r0, #_FpCompareGreater ;; Check if compared > MOVEQ r0, #1 ;; If did, BEQ __ges_Filter_end ;; return true CMP r0, #_FpCompareEqual ;; Check if compared == MOVEQ r0, #1 ;; If did, return true MOVNE r0, #0 ;; else return false __ges_Filter_end IF Interworking :LOR: Thumbing LDMIA sp!, {lr} ;; Return BX lr ELSE LDMIA sp!, {pc} ;; Return ENDIF ENTRY_END __ges ] ;============================================================================== END