/**************************************************************************/ /* mmglue: midipix architecture- and target-specific bits for musl libc */ /* Copyright (C) 2013--2023 SysDeer Technologies, LLC */ /* Released under the Standard MIT License; see COPYING.MMGLUE. */ /**************************************************************************/ .file "fenv.S" /* common exception bits */ #define FE_INVALID (1 << 0) #define FE_DENORM (1 << 1) #define FE_DIVBYZERO (1 << 2) #define FE_OVERFLOW (1 << 3) #define FE_UNDERFLOW (1 << 4) #define FE_INEXACT (1 << 5) #define FE_ALL_EXCEPT (0x3F) /* X87 FPU rounding control bits */ #define FPU_RC_TONEAREST (0x0 << 10) #define FPU_RC_DOWNWARD (0x1 << 10) #define FPU_RC_UPWARD (0x2 << 10) #define FPU_RC_TOWARDZERO (0x3 << 10) #define FPU_RC_MASK FPU_RC_TOWARDZERO /* X87 FPU control word */ #define FPU_CW_MASK (0xFFFF) #define FPU_CW_RC_MASK (FPU_CW_MASK ^ FPU_RC_MASK) /* MXCSR rounding control bits */ #define MXCSR_RC_TONEAREST (0X0 << 13) #define MXCSR_RC_DOWNWARD (0x1 << 13) #define MXCSR_RC_UPWARD (0x2 << 13) #define MXCSR_RC_TOWARDZERO (0x3 << 13) #define MXCSR_RC_MASK MXCSR_RC_TOWARDZERO /* MXCSR control word */ #define MXCSR_CW_MASK (0xFFFF) #define MXCSR_CW_RC_MASK (MXCSR_CW_MASK ^ MXCSR_RC_MASK) /* fenv_At struct definition */ #define FPUCW_MEMBER_OFFSET (0x0) #define MXCSR_MEMBER_OFFSET (0x1C) /* user rounding control flags */ #define FE_RC_TONEAREST FPU_RC_TONEAREST #define FE_RC_DOWNWARD FPU_RC_DOWNWARD #define FE_RC_UPWARD FPU_RC_UPWARD #define FE_RC_TOWARDZERO FPU_RC_TOWARDZERO #define FE_RC_MASK FPU_RC_MASK /* user default float environment */ #define FE_DFL_ENV (-1) /******************************************************/ /* */ /* FSTCW: check for pending floating-point */ /* exceptions, then store FPU control word. */ /* */ /* FNSTCW: store FPU control word without first */ /* checking for pending floating-point */ /* exceptions. */ /* */ /* FSTSW: check for pending floating-point */ /* exceptions, then store FPU status word. */ /* */ /* FNSTSW: store FPU status word without first */ /* checking for pending floating-point */ /* exceptions. */ /* */ /* FCLEX: check for pending floating-point */ /* exceptions, then clear FPU exception */ /* flags. */ /* */ /* FNCLEX: clear FPU exception flags without first */ /* checking for pending floating-point */ /* exceptions. */ /* */ /* FLDCW: set the FPU Control Word from memory. */ /* */ /* FLDENV: set the FPU Environment from memory. */ /* */ /* STMXCSR: store the state of the MXCSR register; */ /* the register's reserved bits are set to */ /* zero in the destination. */ /* */ /* LDMXCSR: set the MXCSR environment from memory. */ /* */ /******************************************************/ /**********************************************/ /* int fetestexcept(int exflags); */ /* */ /* query which exception flags, as indicated */ /* by the exflags mask, are currently set. */ /* */ /**********************************************/ .text .def fetestexcept; .scl 2; .type 32; .endef .global fetestexcept .cfi_startproc; fetestexcept: and $FE_ALL_EXCEPT,%ecx /* normalize exflags */ push %r10 /* temporary state var */ stmxcsr (%rsp) /* store mxcsr state */ pop %rdx /* copy state to rdx */ fnstsw %ax /* store fpu state */ or %edx,%eax /* fpu+mxcsr state */ and %ecx,%eax /* desired flags only */ ret .cfi_endproc /**********************************************/ /* int feclearexcept(int exflags); */ /* */ /* clear specific fpu exception flags, as */ /* indicated by the exflags mask. */ /* */ /**********************************************/ .text .def feclearexcept; .scl 2; .type 32; .endef .global feclearexcept .cfi_startproc; feclearexcept: fnstsw %ax /* store fpu state */ and $FE_ALL_EXCEPT,%ecx /* normalize exflags */ and $FE_ALL_EXCEPT,%eax /* normalize fpu state */ test %ecx,%eax /* test exflags,state */ jz 1f /* state already clear? */ fnclex /* clear fpu state */ 1: push %rdx /* temporary state var */ stmxcsr (%rsp) /* store mxcsr state */ pop %rdx /* copy state to rdx */ or %eax,%edx /* fpu+mxcsr state */ test %ecx,%edx /* test exflags,state */ jz 1f /* state already clear? */ not %ecx /* clear desired flags- */ and %ecx,%edx /* -from mxcsr state */ push %rdx /* modified mxcsr var */ ldmxcsr (%rsp) /* update mxcsr */ pop %rdx /* dealloc mxcsr var */ 1: xor %eax,%eax /* ret zero either way */ ret .cfi_endproc /**********************************************/ /* int feraiseexcept(int exflags); */ /* */ /* set specific fpu exception flags, as */ /* indicated by the exflags mask. */ /* */ /* it is enough to set the flags only in */ /* either mxcsr or the [legacy] x87 fpu, */ /* specifically since fetestexcept() does */ /* take both into account. */ /* */ /**********************************************/ .text .def feraiseexcept; .scl 2; .type 32; .endef .global feraiseexcept .cfi_startproc; feraiseexcept: and $FE_ALL_EXCEPT,%ecx /* normalize exflags */ stmxcsr -0x8(%rsp) /* store mxcsr state */ or %ecx,-0x8(%rsp) /* set desired flags */ ldmxcsr -0x8(%rsp) /* update mxcsr */ xor %eax,%eax ret .cfi_endproc /**********************************************/ /* int fegetround(void); */ /* */ /* obtain the current rounding mode, which */ /* should be one of: round-to-neareset, */ /* round-downwards, round-upwards, or */ /* round-toward-zero. */ /* */ /* when we set the rounding mode we set both */ /* mxcsr and the [legacy] x87 fpu, and it */ /* thus suffices to obtain the mode from */ /* either, which is mxcsr in our case. */ /**********************************************/ .text .def fegetround; .scl 2; .type 32; .endef .global fegetround .cfi_startproc; fegetround: push %rdx /* temporary state var */ stmxcsr (%rsp) /* store mxcsr state */ pop %rax /* copy state to rax */ and $MXCSR_RC_MASK,%rax /* MXCSR RC bits only */ shr $0x3,%rax /* MXCSR --> FE bits */ ret .cfi_endproc /**********************************************/ /* int fesetround(int rcmode); */ /* */ /* set the current rounding mode, which */ /* should be one of: round-to-neareset, */ /* round-downwards, round-upwards, or */ /* round-toward-zero. */ /**********************************************/ .text .def __fesetround; .scl 2; .type 32; .endef .global __fesetround .cfi_startproc; __fesetround: push %rdx /* alloc control var */ and $FE_RC_MASK,%ecx /* normalize rcarg */ fnstcw (%rsp) /* current control word */ andw $FPU_CW_RC_MASK,(%rsp) /* control word only */ or %ecx,(%rsp) /* set rc bits to rcarg */ fldcw (%rsp) /* set fpu control word */ shl $0x3,%ecx /* rcarg: FPU --> MXCSR */ stmxcsr (%rsp) /* current mxcsr reg */ andw $MXCSR_CW_RC_MASK,(%rsp) /* control word only */ or %ecx,(%rsp) /* set rc bits to rcarg */ ldmxcsr (%rsp) /* set mxcsr ctrl word */ pop %rdx /* dealloc control var */ xor %eax,%eax ret .cfi_endproc /**********************************************/ /* int fegetenv(fenv_t *); */ /* */ /* popualte a structure of type fenv_t with */ /* the current X87 FPU environmtnet (fnstenv) */ /* as well as MXCSR environment (stmxcsr). */ /**********************************************/ .text .def fegetenv; .scl 2; .type 32; .endef .global fegetenv .cfi_startproc; fegetenv: fnstenv FPUCW_MEMBER_OFFSET(%rcx) stmxcsr MXCSR_MEMBER_OFFSET(%rcx) xor %eax,%eax ret .cfi_endproc /**********************************************/ /* int fesetenv(cont fenv_t *); */ /* */ /* (re)initialize the X87 FPU and MXCSR */ /* environments from a user structure of */ /* type fenv_t. */ /* */ /* if the pointer to the fenv_t structure is */ /* FE_DFL_ENV, reset the X87 FPU and MXCSR */ /* environments to theit default values as */ /* defined by the architecture. */ /* */ /* .__control_word = 0x037f, */ /* .__unused1 = N/A, */ /* .__status_word = 0x0, */ /* .__unused2 = N/A, */ /* .__tags = 0xffff, */ /* .__unused3 = N/A, */ /* .__eip = 0x0, */ /* .__cs_selector = 0x0, */ /* .__opcode = 0x0, */ /* .__unused4 = N/A, */ /* .__data_offset = 0x0, */ /* .__data_selector = 0x0, */ /* .__unused5 = N/A, */ /* .__mxcsr = 0x1f80 */ /* */ /**********************************************/ .text .def fesetenv; .scl 2; .type 32; .endef .global fesetenv .cfi_startproc; fesetenv: /* prolog */ xor %eax,%eax /* use fenv_t or reset to default values? */ cmpq $FE_DFL_ENV,%rcx jz 1f /* use the user-provided fenv_t */ fldenv FPUCW_MEMBER_OFFSET(%rcx) ldmxcsr MXCSR_MEMBER_OFFSET(%rcx) ret /* reset environments to the architecture-defined defaults */ 1: subq $0x20,%rsp /* X87 FPU */ movq $0x037f,(0x0)(%rsp) movq $0xffff,(0x8)(%rsp) movq $0x0,(0x10)(%rsp) movq $0x0,(0x18)(%rsp) fldenv (%rsp) /* MXCSR */ movq $0x1f80,(%rsp) ldmxcsr (%rsp) /* all done */ addq $0x20,%rsp ret .cfi_endproc .section .got$fetestexcept .global __imp_fetestexcept __imp_fetestexcept: .quad fetestexcept .linkonce discard .section .got$feclearexcept .global __imp_feclearexcept __imp_feclearexcept: .quad feclearexcept .linkonce discard .section .got$feraiseexcept .global __imp_feraiseexcept __imp_feraiseexcept: .quad feraiseexcept .linkonce discard .section .got$fegetround .global __imp_fegetround __imp_fegetround: .quad fegetround .linkonce discard .section .got$__fesetround .global __imp___fesetround __imp___fesetround: .quad __fesetround .linkonce discard .section .got$fegetenv .global __imp_fegetenv __imp_fegetenv: .quad fegetenv .linkonce discard .section .got$fesetenv .global __imp_fesetenv __imp_fesetenv: .quad fesetenv .linkonce discard