Skip to content

Commit baa6051

Browse files
authored
[ fix ] use twos complement truncation for signed ints (#1471)
1 parent 48f3825 commit baa6051

File tree

28 files changed

+2156
-279
lines changed

28 files changed

+2156
-279
lines changed

src/Compiler/ES/ES.idr

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -187,32 +187,24 @@ makeIntBound isBigInt bits =
187187
name = if isBigInt then "bigint_bound_" else "int_bound_"
188188
in addConstToPreamble (name ++ show bits) (f "2" ++ " ** "++ f (show bits))
189189

190-
truncateIntWithBitMask : {auto c : Ref ESs ESSt} -> Int -> String -> Core String
191-
truncateIntWithBitMask bits e =
192-
let bs = show bits
193-
f = adjInt bits
194-
in do ib <- makeIntBound (useBigInt' bits) bits
195-
mn <- addConstToPreamble ("int_mask_neg_" ++ bs) ("-" ++ ib)
196-
mp <- addConstToPreamble ("int_mask_pos_" ++ bs) (ib ++ " - " ++ f "1")
197-
pure $ concat {t = List}
198-
[ "((", ib, " & ", e, ") == " ++ ib ++ " ? "
199-
, "(", e, " | ", mn, ") : "
200-
, "(", e, " & ", mp, ")"
201-
, ")"
202-
]
203-
204-
-- We can't determine `isBigInt` from the given number of bits, since
205-
-- when casting from BigInt to Number we need to truncate the BigInt
206-
-- first, otherwise we might lose precision
207-
boundedInt : {auto c : Ref ESs ESSt} ->
208-
(isBigInt : Bool) -> Int -> String -> Core String
209-
boundedInt isBigInt bits e =
210-
let name = if isBigInt then "truncToBigInt" else "truncToInt"
211-
in do n <- makeIntBound isBigInt bits
212-
fn <- addConstToPreamble
190+
boundedInt : {auto c : Ref ESs ESSt}
191+
-> (isBigInt : Bool)
192+
-> Int
193+
-> String
194+
-> Core String
195+
boundedInt useBigInt bits e =
196+
let bs = show bits
197+
f = if useBigInt then toBigInt else id
198+
name = if useBigInt then "truncToBigInt" else "truncToInt"
199+
in do max <- makeIntBound useBigInt bits
200+
half <- makeIntBound useBigInt (bits - 1)
201+
fn <- addConstToPreamble
213202
(name ++ show bits)
214-
("x=>(x<(-" ++ n ++ ")||(x>=" ++ n ++ "))?x%" ++ n ++ ":x")
215-
pure $ fn ++ "(" ++ e ++ ")"
203+
( concat {t = List}
204+
[ "x=>{ const v = x<",f "0","?x%",max,"+",max,":x%",max,";"
205+
, "return v>=",half,"?","v-",max,":v}"
206+
])
207+
pure $ fn ++ "(" ++ e ++ ")"
216208

217209
boundedUInt : {auto c : Ref ESs ESSt} ->
218210
(isBigInt : Bool) -> Int -> String -> Core String
@@ -229,10 +221,6 @@ boundedIntOp : {auto c : Ref ESs ESSt} ->
229221
boundedIntOp bits o lhs rhs =
230222
boundedInt (useBigInt' bits) bits (binOp o lhs rhs)
231223

232-
boundedIntBitOp : {auto c : Ref ESs ESSt} ->
233-
Int -> String -> String -> String -> Core String
234-
boundedIntBitOp bits o lhs rhs = truncateIntWithBitMask bits (binOp o lhs rhs)
235-
236224
boundedUIntOp : {auto c : Ref ESs ESSt} ->
237225
Int -> String -> String -> String -> Core String
238226
boundedUIntOp bits o lhs rhs =
@@ -267,12 +255,12 @@ mult : {auto c : Ref ESs ESSt}
267255
-> (y : String)
268256
-> Core String
269257
mult (Just $ Signed $ P 32) x y =
270-
fromBigInt <$> boundedInt True 31 (binOp "*" (toBigInt x) (toBigInt y))
258+
fromBigInt <$> boundedInt True 32 (binOp "*" (toBigInt x) (toBigInt y))
271259

272260
mult (Just $ Unsigned 32) x y =
273261
fromBigInt <$> boundedUInt True 32 (binOp "*" (toBigInt x) (toBigInt y))
274262

275-
mult (Just $ Signed $ P n) x y = boundedIntOp (n-1) "*" x y
263+
mult (Just $ Signed $ P n) x y = boundedIntOp n "*" x y
276264
mult (Just $ Unsigned n) x y = boundedUIntOp n "*" x y
277265
mult _ x y = pure $ binOp "*" x y
278266

@@ -281,7 +269,11 @@ div : {auto c : Ref ESs ESSt}
281269
-> (x : String)
282270
-> (y : String)
283271
-> Core String
284-
div (Just k) x y =
272+
div (Just $ Signed $ Unlimited) x y = pure $ binOp "/" x y
273+
div (Just $ k@(Signed $ P n)) x y =
274+
if useBigInt k then boundedIntOp n "/" x y
275+
else boundedInt False n (jsIntOfDouble k (x ++ " / " ++ y))
276+
div (Just $ k@(Unsigned n)) x y =
285277
if useBigInt k then pure $ binOp "/" x y
286278
else pure $ jsIntOfDouble k (x ++ " / " ++ y)
287279
div Nothing x y = pure $ binOp "/" x y
@@ -295,7 +287,7 @@ arithOp : {auto c : Ref ESs ESSt}
295287
-> (x : String)
296288
-> (y : String)
297289
-> Core String
298-
arithOp (Just $ Signed $ P n) op x y = boundedIntOp (n-1) op x y
290+
arithOp (Just $ Signed $ P n) op x y = boundedIntOp n op x y
299291
arithOp (Just $ Unsigned n) op x y = boundedUIntOp n op x y
300292
arithOp _ op x y = pure $ binOp op x y
301293

@@ -310,7 +302,7 @@ bitOp : {auto c : Ref ESs ESSt}
310302
-> (x : String)
311303
-> (y : String)
312304
-> Core String
313-
bitOp (Just $ Signed $ P n) op x y = boundedIntBitOp (n-1) op x y
305+
bitOp (Just $ Signed $ P n) op x y = boundedIntOp n op x y
314306
bitOp (Just $ Unsigned 32) op x y =
315307
fromBigInt <$> boundedUInt True 32 (binOp op (toBigInt x) (toBigInt y))
316308
bitOp (Just $ Unsigned n) op x y = boundedUIntOp n op x y
@@ -328,7 +320,7 @@ constPrimitives = MkConstantPrimitives {
328320
}
329321
where truncInt : (isBigInt : Bool) -> IntKind -> String -> Core String
330322
truncInt b (Signed Unlimited) = pure
331-
truncInt b (Signed $ P n) = boundedInt b (n-1)
323+
truncInt b (Signed $ P n) = boundedInt b n
332324
truncInt b (Unsigned n) = boundedUInt b n
333325

334326
shrink : IntKind -> IntKind -> String -> String

src/Core/Primitives.idr

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ int64max : Integer
106106
int64max = 0x8000000000000000
107107

108108
intCastWrap : (i : Integer) -> (max : Integer) -> Integer
109-
intCastWrap i max
110-
= if i < negate max || i >= max
111-
then i `mod` max
112-
else i
109+
intCastWrap i max =
110+
let max2 = 2*max
111+
i2 = i `mod` max2
112+
i3 = if i2 < 0 then i2 + max2 else i2
113+
in if i3 >= max then i3 - max2 else i3
113114

114115
int8CastWrap : (i : Integer) -> Integer
115116
int8CastWrap i = intCastWrap i int8max

support/chez/support.ss

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010

1111
(define blodwen-toSignedInt
1212
(lambda (x bits)
13-
(let ((ma (ash 1 bits)))
14-
(if (or (< x (- 0 ma))
15-
(>= x ma))
16-
(remainder x ma)
17-
x))))
13+
(if (logbit? bits x)
14+
(logor x (ash (- 1) bits))
15+
(logand x (- (ash 1 bits) 1)))))
1816

1917
(define blodwen-toUnsignedInt
2018
(lambda (x bits)
@@ -47,13 +45,7 @@
4745
(define bits64->bits16 (lambda (x) (modulo x (expt 2 16))))
4846
(define bits64->bits32 (lambda (x) (modulo x (expt 2 32))))
4947

50-
(define truncate-bits
51-
(lambda (x bits)
52-
(if (logbit? bits x)
53-
(logor x (ash (- 1) bits))
54-
(logand x (- (ash 1 bits) 1)))))
55-
56-
(define blodwen-bits-shl-signed (lambda (x y bits) (truncate-bits (ash x y) bits)))
48+
(define blodwen-bits-shl-signed (lambda (x y bits) (blodwen-toSignedInt (ash x y) bits)))
5749

5850
(define blodwen-bits-shl (lambda (x y bits) (remainder (ash x y) (ash 1 bits))))
5951

support/gambit/support.scm

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@
1616

1717
(define blodwen-toSignedInt
1818
(lambda (x bits)
19-
(let ((ma (arithmetic-shift 1 bits)))
20-
(if (or (< x (- 0 ma))
21-
(>= x ma))
22-
(remainder x ma)
23-
x))))
19+
(if (bit-set? bits x)
20+
(bitwise-ior x (arithmetic-shift (- 1) bits))
21+
(bitwise-and x (- (arithmetic-shift 1 bits) 1)))))
2422

2523
(define blodwen-toUnsignedInt
2624
(lambda (x bits)
@@ -65,14 +63,8 @@
6563
(define bits64->bits16 (lambda (x) (modulo x (expt 2 16))))
6664
(define bits64->bits32 (lambda (x) (modulo x (expt 2 32))))
6765

68-
(define truncate-bits
69-
(lambda (x bits)
70-
(if (bit-set? bits x)
71-
(bitwise-ior x (arithmetic-shift (- 1) bits))
72-
(bitwise-and x (- (arithmetic-shift 1 bits) 1)))))
73-
7466
(define blodwen-bits-shl-signed
75-
(lambda (x y bits) (truncate-bits (arithmetic-shift x y) bits)))
67+
(lambda (x y bits) (blodwen-toSignedInt (arithmetic-shift x y) bits)))
7668

7769

7870
(define-macro (blodwen-and . args) `(bitwise-and ,@args))

support/racket/support.rkt

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77

88
(define blodwen-toSignedInt
99
(lambda (x bits)
10-
(let ((ma (arithmetic-shift 1 bits)))
11-
(if (or (< x (- 0 ma))
12-
(>= x ma))
13-
(remainder x ma)
14-
x))))
10+
(if (bitwise-bit-set? x bits)
11+
(bitwise-ior x (arithmetic-shift (- 1) bits))
12+
(bitwise-and x (- (arithmetic-shift 1 bits) 1)))))
1513

1614
(define blodwen-toUnsignedInt
1715
(lambda (x bits)
@@ -55,14 +53,8 @@
5553
(lambda (x)
5654
(inexact->exact (floor x))))
5755

58-
(define truncate-bits
59-
(lambda (x bits)
60-
(if (bitwise-bit-set? x bits)
61-
(bitwise-ior x (arithmetic-shift (- 1) bits))
62-
(bitwise-and x (- (arithmetic-shift 1 bits) 1)))))
63-
6456
(define blodwen-bits-shl-signed
65-
(lambda (x y bits) (truncate-bits (arithmetic-shift x y) bits)))
57+
(lambda (x y bits) (blodwen-toSignedInt (arithmetic-shift x y) bits)))
6658

6759
(define exact-truncate
6860
(lambda (x)

tests/Main.idr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ chezTests = MkTestPool "Chez backend" [Chez]
204204
, "bitops"
205205
, "casts"
206206
, "newints"
207+
, "integers"
207208
, "semaphores001"
208209
, "semaphores002"
209210
, "perf001"
@@ -248,6 +249,7 @@ nodeTests = MkTestPool "Node backend" [Node]
248249
, "syntax001"
249250
, "tailrec001"
250251
, "idiom001"
252+
, "integers"
251253
]
252254

253255
ideModeTests : TestPool

0 commit comments

Comments
 (0)