tree-optimization/118653 - ICE in vectorizable_live_operation
[gcc.git] / libphobos / src / std / math / hardware.d
blob03e5463b5b5c6f4f73a4e3ee4245c6853b0c1ab2
1 // Written in the D programming language.
3 /**
4 This is a submodule of $(MREF std, math).
6 It contains hardware support for floating point numbers.
8 Copyright: Copyright The D Language Foundation 2000 - 2011.
9 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
11 Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
12 Source: $(PHOBOSSRC std/math/hardware.d)
15 /* NOTE: This file has been patched from the original DMD distribution to
16 * work with the GDC compiler.
18 module std.math.hardware;
20 static import core.stdc.fenv;
22 version (X86) version = X86_Any;
23 version (X86_64) version = X86_Any;
24 version (PPC) version = PPC_Any;
25 version (PPC64) version = PPC_Any;
26 version (MIPS32) version = MIPS_Any;
27 version (MIPS64) version = MIPS_Any;
28 version (AArch64) version = ARM_Any;
29 version (ARM) version = ARM_Any;
30 version (S390) version = IBMZ_Any;
31 version (SPARC) version = SPARC_Any;
32 version (SPARC64) version = SPARC_Any;
33 version (SystemZ) version = IBMZ_Any;
34 version (RISCV32) version = RISCV_Any;
35 version (RISCV64) version = RISCV_Any;
36 version (LoongArch64) version = LoongArch_Any;
38 version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
39 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
41 version (X86_64) version = StaticallyHaveSSE;
42 version (X86) version (OSX) version = StaticallyHaveSSE;
44 version (StaticallyHaveSSE)
46 private enum bool haveSSE = true;
48 else version (X86)
50 static import core.cpuid;
51 private alias haveSSE = core.cpuid.sse;
54 version (D_SoftFloat)
56 // Some soft float implementations may support IEEE floating flags.
57 // The implementation here supports hardware flags only and is so currently
58 // only available for supported targets.
60 else version (X86_Any) version = IeeeFlagsSupport;
61 else version (PPC_Any) version = IeeeFlagsSupport;
62 else version (RISCV_Any) version = IeeeFlagsSupport;
63 else version (MIPS_Any) version = IeeeFlagsSupport;
64 else version (LoongArch_Any) version = IeeeFlagsSupport;
65 else version (ARM_Any) version = IeeeFlagsSupport;
67 // Struct FloatingPointControl is only available if hardware FP units are available.
68 version (D_HardFloat)
70 // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
71 version (IeeeFlagsSupport) version = FloatingPointControlSupport;
74 version (IeeeFlagsSupport)
77 /** IEEE exception status flags ('sticky bits')
79 These flags indicate that an exceptional floating-point condition has occurred.
80 They indicate that a NaN or an infinity has been generated, that a result
81 is inexact, or that a signalling NaN has been encountered. If floating-point
82 exceptions are enabled (unmasked), a hardware exception will be generated
83 instead of setting these flags.
85 struct IeeeFlags
87 nothrow @nogc:
89 private:
90 // The x87 FPU status register is 16 bits.
91 // The Pentium SSE2 status register is 32 bits.
92 // The ARM and PowerPC FPSCR is a 32-bit register.
93 // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
94 // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
95 // THe LoongArch fcsr (fcsr0) is a 32-bit register.
96 uint flags;
98 version (CRuntime_Microsoft)
100 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
101 // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
102 enum : int
104 INEXACT_MASK = 0x20,
105 UNDERFLOW_MASK = 0x10,
106 OVERFLOW_MASK = 0x08,
107 DIVBYZERO_MASK = 0x04,
108 INVALID_MASK = 0x01,
110 EXCEPTIONS_MASK = 0b11_1111
112 // Don't bother about subnormals, they are not supported on most CPUs.
113 // SUBNORMAL_MASK = 0x02;
115 else
117 enum : int
119 INEXACT_MASK = core.stdc.fenv.FE_INEXACT,
120 UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW,
121 OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW,
122 DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO,
123 INVALID_MASK = core.stdc.fenv.FE_INVALID,
124 EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
128 static uint getIeeeFlags() @trusted pure
130 version (GNU)
132 version (X86_Any)
134 ushort sw;
135 asm pure nothrow @nogc
137 "fstsw %0" : "=a" (sw);
139 // OR the result with the SSE2 status register (MXCSR).
140 if (haveSSE)
142 uint mxcsr;
143 asm pure nothrow @nogc
145 "stmxcsr %0" : "=m" (mxcsr);
147 return (sw | mxcsr) & EXCEPTIONS_MASK;
149 else
150 return sw & EXCEPTIONS_MASK;
152 else version (ARM)
154 version (ARM_SoftFloat)
155 return 0;
156 else
158 uint result = void;
159 asm pure nothrow @nogc
161 "vmrs %0, FPSCR; and %0, %0, #0x1F;" : "=r" (result);
163 return result;
166 else version (RISCV_Any)
168 version (D_SoftFloat)
169 return 0;
170 else
172 uint result = void;
173 asm pure nothrow @nogc
175 "frflags %0" : "=r" (result);
177 return result;
180 else
181 assert(0, "Not yet supported");
183 else
184 version (InlineAsm_X86_Any)
186 ushort sw;
187 asm pure nothrow @nogc { fstsw sw; }
189 // OR the result with the SSE2 status register (MXCSR).
190 if (haveSSE)
192 uint mxcsr;
193 asm pure nothrow @nogc { stmxcsr mxcsr; }
194 return (sw | mxcsr) & EXCEPTIONS_MASK;
196 else return sw & EXCEPTIONS_MASK;
198 else version (SPARC)
201 int retval;
202 asm pure nothrow @nogc { st %fsr, retval; }
203 return retval;
205 assert(0, "Not yet supported");
207 else version (ARM)
209 assert(false, "Not yet supported.");
211 else version (RISCV_Any)
213 uint result = void;
214 asm pure nothrow @nogc
216 "frflags %0" : "=r" (result);
218 return result;
220 else version (LoongArch_Any)
222 uint result = void;
223 asm pure nothrow @nogc
225 "movfcsr2gr %0, $fcsr2" : "=r" (result);
227 return result & EXCEPTIONS_MASK;
229 else
230 assert(0, "Not yet supported");
233 static void resetIeeeFlags() @trusted
235 version (GNU)
237 version (X86_Any)
239 asm nothrow @nogc
241 "fnclex";
244 // Also clear exception flags in MXCSR, SSE's control register.
245 if (haveSSE)
247 uint mxcsr;
248 asm nothrow @nogc
250 "stmxcsr %0" : "=m" (mxcsr);
252 mxcsr &= ~EXCEPTIONS_MASK;
253 asm nothrow @nogc
255 "ldmxcsr %0" : : "m" (mxcsr);
259 else version (ARM)
261 version (ARM_SoftFloat)
262 return;
263 else
265 uint old = FloatingPointControl.getControlState();
266 old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
267 asm nothrow @nogc
269 "vmsr FPSCR, %0" : : "r" (old);
273 else version (RISCV_Any)
275 version (D_SoftFloat)
276 return;
277 else
279 uint newValues = 0x0;
280 asm nothrow @nogc
282 "fsflags %0" : : "r" (newValues);
286 else
287 assert(0, "Not yet supported");
289 else
290 version (InlineAsm_X86_Any)
292 asm nothrow @nogc
294 fnclex;
297 // Also clear exception flags in MXCSR, SSE's control register.
298 if (haveSSE)
300 uint mxcsr;
301 asm nothrow @nogc { stmxcsr mxcsr; }
302 mxcsr &= ~EXCEPTIONS_MASK;
303 asm nothrow @nogc { ldmxcsr mxcsr; }
306 else version (RISCV_Any)
308 uint newValues = 0x0;
309 asm pure nothrow @nogc
311 "fsflags %0" : : "r" (newValues);
314 else version (LoongArch_Any)
316 asm nothrow @nogc
318 "movgr2fcsr $fcsr2,$r0";
321 else
323 /* SPARC:
324 int tmpval;
325 asm pure nothrow @nogc { st %fsr, tmpval; }
326 tmpval &=0xFFFF_FC00;
327 asm pure nothrow @nogc { ld tmpval, %fsr; }
329 assert(0, "Not yet supported");
333 public:
335 * The result cannot be represented exactly, so rounding occurred.
336 * Example: `x = sin(0.1);`
338 @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; }
341 * A zero was generated by underflow
342 * Example: `x = real.min*real.epsilon/2;`
344 @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; }
347 * An infinity was generated by overflow
348 * Example: `x = real.max*2;`
350 @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; }
353 * An infinity was generated by division by zero
354 * Example: `x = 3/0.0;`
356 @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; }
359 * A machine NaN was generated.
360 * Example: `x = real.infinity * 0.0;`
362 @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; }
366 version (StdDdoc)
367 @safe unittest
369 import std.math.traits : isNaN;
371 static void func() {
372 int a = 10 * 10;
374 real a = 3.5;
375 // Set all the flags to zero
376 resetIeeeFlags();
377 assert(!ieeeFlags.divByZero);
378 // Perform a division by zero.
379 a /= 0.0L;
380 assert(a == real.infinity);
381 assert(ieeeFlags.divByZero);
382 // Create a NaN
383 a *= 0.0L;
384 assert(ieeeFlags.invalid);
385 assert(isNaN(a));
387 // Check that calling func() has no effect on the
388 // status flags.
389 IeeeFlags f = ieeeFlags;
390 func();
391 assert(ieeeFlags == f);
394 @safe unittest
396 import std.math.traits : isNaN;
398 static void func() {
399 int a = 10 * 10;
401 real a = 3.5;
402 // Set all the flags to zero
403 resetIeeeFlags();
404 assert(!ieeeFlags.divByZero);
405 // Perform a division by zero.
406 a = forceDivOp(a, 0.0L);
407 assert(a == real.infinity);
408 assert(ieeeFlags.divByZero);
409 // Create a NaN
410 a = forceMulOp(a, 0.0L);
411 assert(ieeeFlags.invalid);
412 assert(isNaN(a));
414 // Check that calling func() has no effect on the
415 // status flags.
416 IeeeFlags f = ieeeFlags;
417 func();
418 assert(ieeeFlags == f);
421 @safe unittest
423 import std.meta : AliasSeq;
425 static struct Test
427 void delegate() @trusted action;
428 bool function() @trusted ieeeCheck;
431 static foreach (T; AliasSeq!(float, double, real))
433 T x; // Needs to be here to avoid `call without side effects` warning.
434 auto tests = [
435 Test(
436 () { x = forceAddOp!T(1, 0.1L); },
437 () => ieeeFlags.inexact
439 Test(
440 () { x = forceDivOp!T(T.min_normal, T.max); },
441 () => ieeeFlags.underflow
443 Test(
444 () { x = forceAddOp!T(T.max, T.max); },
445 () => ieeeFlags.overflow
447 Test(
448 () { x = forceDivOp!T(1, 0); },
449 () => ieeeFlags.divByZero
451 Test(
452 () { x = forceDivOp!T(0, 0); },
453 () => ieeeFlags.invalid
456 foreach (test; tests)
458 resetIeeeFlags();
459 assert(!test.ieeeCheck());
460 test.action();
461 assert(test.ieeeCheck());
466 /// Set all of the floating-point status flags to false.
467 void resetIeeeFlags() @trusted nothrow @nogc
469 IeeeFlags.resetIeeeFlags();
473 version (StdDdoc)
474 @safe unittest
476 resetIeeeFlags();
477 real a = 3.5;
478 a /= 0.0L;
479 assert(a == real.infinity);
480 assert(ieeeFlags.divByZero);
482 resetIeeeFlags();
483 assert(!ieeeFlags.divByZero);
486 @safe unittest
488 resetIeeeFlags();
489 real a = 3.5;
490 a = forceDivOp(a, 0.0L);
491 assert(a == real.infinity);
492 assert(ieeeFlags.divByZero);
494 resetIeeeFlags();
495 assert(!ieeeFlags.divByZero);
498 /// Returns: snapshot of the current state of the floating-point status flags
499 @property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc
501 return IeeeFlags(IeeeFlags.getIeeeFlags());
505 version (StdDdoc)
506 @safe nothrow unittest
508 import std.math.traits : isNaN;
510 resetIeeeFlags();
511 real a = 3.5;
513 a /= 0.0L;
514 assert(a == real.infinity);
515 assert(ieeeFlags.divByZero);
517 a *= 0.0L;
518 assert(isNaN(a));
519 assert(ieeeFlags.invalid);
522 @safe nothrow unittest
524 import std.math.traits : isNaN;
526 resetIeeeFlags();
527 real a = 3.5;
529 a = forceDivOp(a, 0.0L);
530 assert(a == real.infinity);
531 assert(ieeeFlags.divByZero);
533 a = forceMulOp(a, 0.0L);
534 assert(isNaN(a));
535 assert(ieeeFlags.invalid);
538 } // IeeeFlagsSupport
541 version (FloatingPointControlSupport)
544 /** Control the Floating point hardware
546 Change the IEEE754 floating-point rounding mode and the floating-point
547 hardware exceptions.
549 By default, the rounding mode is roundToNearest and all hardware exceptions
550 are disabled. For most applications, debugging is easier if the $(I division
551 by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
552 These three are combined into a $(I severeExceptions) value for convenience.
553 Note in particular that if $(I invalidException) is enabled, a hardware trap
554 will be generated whenever an uninitialized floating-point variable is used.
556 All changes are temporary. The previous state is restored at the
557 end of the scope.
560 Example:
561 ----
563 FloatingPointControl fpctrl;
565 // Enable hardware exceptions for division by zero, overflow to infinity,
566 // invalid operations, and uninitialized floating-point variables.
567 fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
569 // This will generate a hardware exception, if x is a
570 // default-initialized floating point variable:
571 real x; // Add `= 0` or even `= real.nan` to not throw the exception.
572 real y = x * 3.0;
574 // The exception is only thrown for default-uninitialized NaN-s.
575 // NaN-s with other payload are valid:
576 real z = y * real.nan; // ok
578 // The set hardware exceptions and rounding modes will be disabled when
579 // leaving this scope.
581 ----
584 struct FloatingPointControl
586 nothrow @nogc:
588 alias RoundingMode = uint; ///
590 version (StdDdoc)
592 enum : RoundingMode
594 /** IEEE rounding modes.
595 * The default mode is roundToNearest.
597 * roundingMask = A mask of all rounding modes.
599 roundToNearest,
600 roundDown, /// ditto
601 roundUp, /// ditto
602 roundToZero, /// ditto
603 roundingMask, /// ditto
606 else version (CRuntime_Microsoft)
608 // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
609 enum : RoundingMode
611 roundToNearest = 0x0000,
612 roundDown = 0x0400,
613 roundUp = 0x0800,
614 roundToZero = 0x0C00,
615 roundingMask = roundToNearest | roundDown
616 | roundUp | roundToZero,
619 else
621 enum : RoundingMode
623 roundToNearest = core.stdc.fenv.FE_TONEAREST,
624 roundDown = core.stdc.fenv.FE_DOWNWARD,
625 roundUp = core.stdc.fenv.FE_UPWARD,
626 roundToZero = core.stdc.fenv.FE_TOWARDZERO,
627 roundingMask = roundToNearest | roundDown
628 | roundUp | roundToZero,
632 /***
633 * Change the floating-point hardware rounding mode
635 * Changing the rounding mode in the middle of a function can interfere
636 * with optimizations of floating point expressions, as the optimizer assumes
637 * that the rounding mode does not change.
638 * It is best to change the rounding mode only at the
639 * beginning of the function, and keep it until the function returns.
640 * It is also best to add the line:
641 * ---
642 * pragma(inline, false);
643 * ---
644 * as the first line of the function so it will not get inlined.
645 * Params:
646 * newMode = the new rounding mode
648 @property void rounding(RoundingMode newMode) @trusted
650 initialize();
651 setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask));
654 /// Returns: the currently active rounding mode
655 @property static RoundingMode rounding() @trusted pure
657 return cast(RoundingMode)(getControlState() & roundingMask);
660 alias ExceptionMask = uint; ///
662 version (StdDdoc)
664 enum : ExceptionMask
666 /** IEEE hardware exceptions.
667 * By default, all exceptions are masked (disabled).
669 * severeExceptions = The overflow, division by zero, and invalid
670 * exceptions.
672 subnormalException,
673 inexactException, /// ditto
674 underflowException, /// ditto
675 overflowException, /// ditto
676 divByZeroException, /// ditto
677 invalidException, /// ditto
678 severeExceptions, /// ditto
679 allExceptions, /// ditto
682 else version (ARM_Any)
684 enum : ExceptionMask
686 subnormalException = 0x8000,
687 inexactException = 0x1000,
688 underflowException = 0x0800,
689 overflowException = 0x0400,
690 divByZeroException = 0x0200,
691 invalidException = 0x0100,
692 severeExceptions = overflowException | divByZeroException
693 | invalidException,
694 allExceptions = severeExceptions | underflowException
695 | inexactException | subnormalException,
698 else version (PPC_Any)
700 enum : ExceptionMask
702 inexactException = 0x0008,
703 divByZeroException = 0x0010,
704 underflowException = 0x0020,
705 overflowException = 0x0040,
706 invalidException = 0x0080,
707 severeExceptions = overflowException | divByZeroException
708 | invalidException,
709 allExceptions = severeExceptions | underflowException
710 | inexactException,
713 else version (RISCV_Any)
715 enum : ExceptionMask
717 inexactException = 0x01,
718 divByZeroException = 0x08,
719 underflowException = 0x02,
720 overflowException = 0x04,
721 invalidException = 0x10,
722 severeExceptions = overflowException | divByZeroException
723 | invalidException,
724 allExceptions = severeExceptions | underflowException
725 | inexactException,
728 else version (HPPA)
730 enum : ExceptionMask
732 inexactException = 0x01,
733 underflowException = 0x02,
734 overflowException = 0x04,
735 divByZeroException = 0x08,
736 invalidException = 0x10,
737 severeExceptions = overflowException | divByZeroException
738 | invalidException,
739 allExceptions = severeExceptions | underflowException
740 | inexactException,
743 else version (LoongArch_Any)
745 enum : ExceptionMask
747 inexactException = 0x00,
748 divByZeroException = 0x01,
749 overflowException = 0x02,
750 underflowException = 0x04,
751 invalidException = 0x08,
752 severeExceptions = overflowException | divByZeroException
753 | invalidException,
754 allExceptions = severeExceptions | underflowException
755 | inexactException,
758 else version (MIPS_Any)
760 enum : ExceptionMask
762 inexactException = 0x0080,
763 divByZeroException = 0x0400,
764 overflowException = 0x0200,
765 underflowException = 0x0100,
766 invalidException = 0x0800,
767 severeExceptions = overflowException | divByZeroException
768 | invalidException,
769 allExceptions = severeExceptions | underflowException
770 | inexactException,
773 else version (SPARC_Any)
775 enum : ExceptionMask
777 inexactException = 0x0800000,
778 divByZeroException = 0x1000000,
779 overflowException = 0x4000000,
780 underflowException = 0x2000000,
781 invalidException = 0x8000000,
782 severeExceptions = overflowException | divByZeroException
783 | invalidException,
784 allExceptions = severeExceptions | underflowException
785 | inexactException,
788 else version (IBMZ_Any)
790 enum : ExceptionMask
792 inexactException = 0x08000000,
793 divByZeroException = 0x40000000,
794 overflowException = 0x20000000,
795 underflowException = 0x10000000,
796 invalidException = 0x80000000,
797 severeExceptions = overflowException | divByZeroException
798 | invalidException,
799 allExceptions = severeExceptions | underflowException
800 | inexactException,
803 else version (X86_Any)
805 enum : ExceptionMask
807 inexactException = 0x20,
808 underflowException = 0x10,
809 overflowException = 0x08,
810 divByZeroException = 0x04,
811 subnormalException = 0x02,
812 invalidException = 0x01,
813 severeExceptions = overflowException | divByZeroException
814 | invalidException,
815 allExceptions = severeExceptions | underflowException
816 | inexactException | subnormalException,
819 else
820 static assert(false, "Not implemented for this architecture");
822 version (ARM_Any)
824 static bool hasExceptionTraps_impl() @safe
826 auto oldState = getControlState();
827 // If exceptions are not supported, we set the bit but read it back as zero
828 // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
829 setControlState(oldState | divByZeroException);
830 immutable result = (getControlState() & allExceptions) != 0;
831 setControlState(oldState);
832 return result;
836 /// Returns: true if the current FPU supports exception trapping
837 @property static bool hasExceptionTraps() @safe pure
839 version (X86_Any)
840 return true;
841 else version (PPC_Any)
842 return true;
843 else version (MIPS_Any)
844 return true;
845 else version (LoongArch_Any)
846 return true;
847 else version (ARM_Any)
849 // The hasExceptionTraps_impl function is basically pure,
850 // as it restores all global state
851 auto fptr = ( () @trusted => cast(bool function() @safe
852 pure nothrow @nogc)&hasExceptionTraps_impl)();
853 return fptr();
855 else
856 assert(0, "Not yet supported");
859 /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
860 void enableExceptions(ExceptionMask exceptions) @trusted
862 assert(hasExceptionTraps);
863 initialize();
864 version (X86_Any)
865 setControlState(getControlState() & ~(exceptions & allExceptions));
866 else
867 setControlState(getControlState() | (exceptions & allExceptions));
870 /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
871 void disableExceptions(ExceptionMask exceptions) @trusted
873 assert(hasExceptionTraps);
874 initialize();
875 version (X86_Any)
876 setControlState(getControlState() | (exceptions & allExceptions));
877 else
878 setControlState(getControlState() & ~(exceptions & allExceptions));
881 /// Returns: the exceptions which are currently enabled (unmasked)
882 @property static ExceptionMask enabledExceptions() @trusted pure
884 assert(hasExceptionTraps);
885 version (X86_Any)
886 return (getControlState() & allExceptions) ^ allExceptions;
887 else
888 return (getControlState() & allExceptions);
891 /// Clear all pending exceptions, then restore the original exception state and rounding mode.
892 ~this() @trusted
894 clearExceptions();
895 if (initialized)
896 setControlState(savedState);
899 private:
900 ControlState savedState;
902 bool initialized = false;
904 version (ARM_Any)
906 alias ControlState = uint;
908 else version (HPPA)
910 alias ControlState = uint;
912 else version (PPC_Any)
914 alias ControlState = uint;
916 else version (RISCV_Any)
918 alias ControlState = uint;
920 else version (LoongArch_Any)
922 alias ControlState = uint;
924 else version (MIPS_Any)
926 alias ControlState = uint;
928 else version (SPARC_Any)
930 alias ControlState = ulong;
932 else version (IBMZ_Any)
934 alias ControlState = uint;
936 else version (X86_Any)
938 alias ControlState = ushort;
940 else
941 static assert(false, "Not implemented for this architecture");
943 void initialize() @safe
945 // BUG: This works around the absence of this() constructors.
946 if (initialized) return;
947 clearExceptions();
948 savedState = getControlState();
949 initialized = true;
952 // Clear all pending exceptions
953 static void clearExceptions() @safe
955 version (IeeeFlagsSupport)
956 resetIeeeFlags();
957 else
958 static assert(false, "Not implemented for this architecture");
961 // Read from the control register
962 package(std.math) static ControlState getControlState() @trusted pure
964 version (GNU)
966 version (X86_Any)
968 ControlState cont;
969 asm pure nothrow @nogc
971 "fstcw %0" : "=m" (cont);
973 return cont;
975 else version (AArch64)
977 ControlState cont;
978 asm pure nothrow @nogc
980 "mrs %0, FPCR;" : "=r" (cont);
982 return cont;
984 else version (ARM)
986 ControlState cont;
987 version (ARM_SoftFloat)
988 cont = 0;
989 else
991 asm pure nothrow @nogc
993 "vmrs %0, FPSCR" : "=r" (cont);
996 return cont;
998 else version (RISCV_Any)
1000 version (D_SoftFloat)
1001 return 0;
1002 else
1004 ControlState cont;
1005 asm pure nothrow @nogc
1007 "frcsr %0" : "=r" (cont);
1009 return cont;
1012 else
1013 assert(0, "Not yet supported");
1015 else
1016 version (D_InlineAsm_X86)
1018 short cont;
1019 asm pure nothrow @nogc
1021 xor EAX, EAX;
1022 fstcw cont;
1024 return cont;
1026 else version (D_InlineAsm_X86_64)
1028 short cont;
1029 asm pure nothrow @nogc
1031 xor RAX, RAX;
1032 fstcw cont;
1034 return cont;
1036 else version (RISCV_Any)
1038 ControlState cont;
1039 asm pure nothrow @nogc
1041 "frcsr %0" : "=r" (cont);
1043 return cont;
1045 else version (LoongArch_Any)
1047 ControlState cont;
1048 asm pure nothrow @nogc
1050 "movfcsr2gr %0, $fcsr0" : "=r" (cont);
1052 cont &= (roundingMask | allExceptions);
1053 return cont;
1055 else
1056 assert(0, "Not yet supported");
1059 // Set the control register
1060 package(std.math) static void setControlState(ControlState newState) @trusted
1062 version (GNU)
1064 version (X86_Any)
1066 asm nothrow @nogc
1068 "fclex; fldcw %0" : : "m" (newState);
1071 // Also update MXCSR, SSE's control register.
1072 if (haveSSE)
1074 uint mxcsr;
1075 asm nothrow @nogc
1077 "stmxcsr %0" : "=m" (mxcsr);
1080 /* In the FPU control register, rounding mode is in bits 10 and
1081 11. In MXCSR it's in bits 13 and 14. */
1082 mxcsr &= ~(roundingMask << 3); // delete old rounding mode
1083 mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
1085 /* In the FPU control register, masks are bits 0 through 5.
1086 In MXCSR they're 7 through 12. */
1087 mxcsr &= ~(allExceptions << 7); // delete old masks
1088 mxcsr |= (newState & allExceptions) << 7; // write new exception masks
1090 asm nothrow @nogc
1092 "ldmxcsr %0" : : "m" (mxcsr);
1096 else version (AArch64)
1098 asm nothrow @nogc
1100 "msr FPCR, %0;" : : "r" (newState);
1103 else version (ARM)
1105 version (ARM_SoftFloat)
1106 return;
1107 else
1109 asm nothrow @nogc
1111 "vmsr FPSCR, %0" : : "r" (newState);
1115 else version (RISCV_Any)
1117 version (D_SoftFloat)
1118 return;
1119 else
1121 asm nothrow @nogc
1123 "fscsr %0" : : "r" (newState);
1127 else
1128 assert(0, "Not yet supported");
1130 else
1131 version (InlineAsm_X86_Any)
1133 asm nothrow @nogc
1135 fclex;
1136 fldcw newState;
1139 // Also update MXCSR, SSE's control register.
1140 if (haveSSE)
1142 uint mxcsr;
1143 asm nothrow @nogc { stmxcsr mxcsr; }
1145 /* In the FPU control register, rounding mode is in bits 10 and
1146 11. In MXCSR it's in bits 13 and 14. */
1147 mxcsr &= ~(roundingMask << 3); // delete old rounding mode
1148 mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
1150 /* In the FPU control register, masks are bits 0 through 5.
1151 In MXCSR they're 7 through 12. */
1152 mxcsr &= ~(allExceptions << 7); // delete old masks
1153 mxcsr |= (newState & allExceptions) << 7; // write new exception masks
1155 asm nothrow @nogc { ldmxcsr mxcsr; }
1158 else version (RISCV_Any)
1160 asm pure nothrow @nogc
1162 "fscsr %0" : : "r" (newState);
1165 else version (LoongArch_Any)
1167 asm nothrow @nogc
1169 "movgr2fcsr $fcsr0,%0" :
1170 : "r" (newState & (roundingMask | allExceptions));
1173 else
1174 assert(0, "Not yet supported");
1179 @safe unittest
1181 import std.math.rounding : lrint;
1183 FloatingPointControl fpctrl;
1185 fpctrl.rounding = FloatingPointControl.roundDown;
1186 assert(lrint(1.5) == 1.0);
1188 fpctrl.rounding = FloatingPointControl.roundUp;
1189 assert(lrint(1.4) == 2.0);
1191 fpctrl.rounding = FloatingPointControl.roundToNearest;
1192 assert(lrint(1.5) == 2.0);
1195 @safe unittest
1197 void ensureDefaults()
1199 assert(FloatingPointControl.rounding
1200 == FloatingPointControl.roundToNearest);
1201 if (FloatingPointControl.hasExceptionTraps)
1202 assert(FloatingPointControl.enabledExceptions == 0);
1206 FloatingPointControl ctrl;
1208 ensureDefaults();
1211 FloatingPointControl ctrl;
1212 ctrl.rounding = FloatingPointControl.roundDown;
1213 assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
1215 ensureDefaults();
1217 if (FloatingPointControl.hasExceptionTraps)
1219 FloatingPointControl ctrl;
1220 ctrl.enableExceptions(FloatingPointControl.divByZeroException
1221 | FloatingPointControl.overflowException);
1222 assert(ctrl.enabledExceptions ==
1223 (FloatingPointControl.divByZeroException
1224 | FloatingPointControl.overflowException));
1226 ctrl.rounding = FloatingPointControl.roundUp;
1227 assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
1229 ensureDefaults();
1232 @safe unittest // rounding
1234 import std.meta : AliasSeq;
1236 static T addRound(T)(uint rm)
1238 pragma(inline, false);
1239 FloatingPointControl fpctrl;
1240 fpctrl.rounding = rm;
1241 T x = 1;
1242 x = forceAddOp(x, 0.1L);
1243 return x;
1246 static T subRound(T)(uint rm)
1248 pragma(inline, false);
1249 FloatingPointControl fpctrl;
1250 fpctrl.rounding = rm;
1251 T x = -1;
1252 x = forceSubOp(x, 0.1L);
1253 return x;
1256 static foreach (T; AliasSeq!(float, double, real))
1258 /* Be careful with changing the rounding mode, it interferes
1259 * with common subexpressions. Changing rounding modes should
1260 * be done with separate functions that are not inlined.
1264 T u = addRound!(T)(FloatingPointControl.roundUp);
1265 T d = addRound!(T)(FloatingPointControl.roundDown);
1266 T z = addRound!(T)(FloatingPointControl.roundToZero);
1268 assert(u > d);
1269 assert(z == d);
1273 T u = subRound!(T)(FloatingPointControl.roundUp);
1274 T d = subRound!(T)(FloatingPointControl.roundDown);
1275 T z = subRound!(T)(FloatingPointControl.roundToZero);
1277 assert(u > d);
1278 assert(z == u);
1283 } // FloatingPointControlSupport
1285 version (StdUnittest)
1287 // These helpers are intended to avoid constant propagation by the optimizer.
1288 pragma(inline, false) private @safe
1290 T forceAddOp(T)(T x, T y) { return x + y; }
1291 T forceSubOp(T)(T x, T y) { return x - y; }
1292 T forceMulOp(T)(T x, T y) { return x * y; }
1293 T forceDivOp(T)(T x, T y) { return x / y; }