summary refs log tree commit diff stats
path: root/lib/system/integerops.nim
blob: 4ef3594f11842bb0927f9813935d6f3dbafcf5b2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#
#
#            Nim's Runtime Library
#        (c) Copyright 2020 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Integer arithmetic with overflow checking. Uses
# intrinsics or inline assembler.

proc raiseOverflow {.compilerproc, noinline.} =
  # a single proc to reduce code size to a minimum
  sysFatal(OverflowDefect, "over- or underflow")

proc raiseDivByZero {.compilerproc, noinline.} =
  sysFatal(DivByZeroDefect, "division by zero")

{.pragma: nimbaseH, importc, nodecl, noSideEffect, compilerproc.}

when not defined(nimEmulateOverflowChecks):
  # take the #define from nimbase.h

  proc nimAddInt(a, b: int, res: ptr int): bool {.nimbaseH.}
  proc nimSubInt(a, b: int, res: ptr int): bool {.nimbaseH.}
  proc nimMulInt(a, b: int, res: ptr int): bool {.nimbaseH.}

  proc nimAddInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  proc nimSubInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
  proc nimMulInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}

# unary minus and 'abs' not required here anymore and are directly handled
# in the code generator.
# 'nimModInt' does exist in nimbase.h without check as we moved the
# check for 0 to the codgen.
proc nimModInt(a, b: int; res: ptr int): bool {.nimbaseH.}

proc nimModInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}

# Platform independent versions.

template addImplFallback(name, T, U) {.dirty.} =
  when not declared(name):
    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
      let r = cast[T](cast[U](a) + cast[U](b))
      if (r xor a) >= T(0) or (r xor b) >= T(0):
        res[] = r
      else:
        result = true

addImplFallback(nimAddInt, int, uint)
addImplFallback(nimAddInt64, int64, uint64)

template subImplFallback(name, T, U) {.dirty.} =
  when not declared(name):
    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
      let r = cast[T](cast[U](a) - cast[U](b))
      if (r xor a) >= 0 or (r xor not b) >= 0:
        res[] = r
      else:
        result = true

subImplFallback(nimSubInt, int, uint)
subImplFallback(nimSubInt64, int64, uint64)

template mulImplFallback(name, T, U, conv) {.dirty.} =
  #
  # This code has been inspired by Python's source code.
  # The native int product x*y is either exactly right or *way* off, being
  # just the last n bits of the true product, where n is the number of bits
  # in an int (the delivered product is the true product plus i*2**n for
  # some integer i).
  #
  # The native float64 product x*y is subject to three
  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
  # But, unlike the native int product, it's not in *range* trouble:  even
  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
  # product are correct.
  #
  # We check these two ways against each other, and declare victory if
  # they're approximately the same. Else, because the native int product is
  # the only one that can lose catastrophic amounts of information, it's the
  # native int product that must have overflowed.
  #
  when not declared(name):
    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
      let r = cast[T](cast[U](a) * cast[U](b))
      let floatProd = conv(a) * conv(b)
      let resAsFloat = conv(r)
      # Fast path for normal case: small multiplicands, and no info
      # is lost in either method.
      if resAsFloat == floatProd:
        res[] = r
      else:
        # Somebody somewhere lost info. Close enough, or way off? Note
        # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
        # The difference either is or isn't significant compared to the
        # true value (of which floatProd is a good approximation).

        # abs(diff)/abs(prod) <= 1/32 iff
        #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
        if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
          res[] = r
        else:
          result = true

mulImplFallback(nimMulInt, int, uint, toFloat)
mulImplFallback(nimMulInt64, int64, uint64, toBiggestFloat)


template divImplFallback(name, T) {.dirty.} =
  proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
    # we moved the b == 0 case out into the codegen.
    if a == low(T) and b == T(-1):
      result = true
    else:
      res[] = a div b

divImplFallback(nimDivInt, int)
divImplFallback(nimDivInt64, int64)

proc raiseFloatInvalidOp {.compilerproc, noinline.} =
  sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result")

proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
  if x > 0.0:
    sysFatal(FloatOverflowDefect, "FPU operation caused an overflow")
  else:
    sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow")