Add signed word iteration
This commit is contained in:
parent
97fbe56353
commit
e2ab2dbf59
|
@ -0,0 +1,134 @@
|
|||
# Mpint
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes, macros, as_words
|
||||
|
||||
proc optimInt*(x: NimNode): NimNode =
|
||||
let size = getSize(x)
|
||||
|
||||
if size > 64:
|
||||
result = quote do:
|
||||
# We represent as unsigned int. Signedness will be managed at a higher level.
|
||||
array[`size` div 64, uint64]
|
||||
elif size == 64:
|
||||
result = quote do:
|
||||
int64
|
||||
elif size == 32:
|
||||
result = quote do:
|
||||
int32
|
||||
elif size == 16:
|
||||
result = quote do:
|
||||
int16
|
||||
elif size == 8:
|
||||
result = quote do:
|
||||
int8
|
||||
else:
|
||||
error "Unreachable path reached"
|
||||
|
||||
proc isInt*(x: NimNode): static[bool] =
|
||||
if eqIdent(x, "int64"): true
|
||||
elif eqIdent(x, "int32"): true
|
||||
elif eqIdent(x, "int16"): true
|
||||
elif eqIdent(x, "int8"): true
|
||||
else: false
|
||||
|
||||
macro most_significant_word*(x: IntImpl): untyped =
|
||||
|
||||
let optim_type = optimInt(x)
|
||||
if optim_type.isInt:
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)[`msw_pos`]
|
||||
|
||||
macro asSignedWordsZip*[T](
|
||||
x, y: IntImpl[T],
|
||||
loopBody: untyped): untyped =
|
||||
## Iterates over x and y, as an array of words.
|
||||
## Input:
|
||||
## - x, y: The multiprecision ints
|
||||
## - loopBody: the operation you want to do.
|
||||
## For the most significant word,
|
||||
## the operation will be sign aware.
|
||||
## for the next words it will ignore sign.
|
||||
## Iteration is always done from most significant to least significant
|
||||
let
|
||||
optim_type = optimInt(x)
|
||||
idx = ident("idx_asSignedWordsRawZip")
|
||||
var
|
||||
first_x, first_y: NimNode
|
||||
next_x, next_y: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
to_replace.add x
|
||||
to_replace.add y
|
||||
|
||||
# We directly cast the first x and y if the result fits in a word
|
||||
# Otherwise we special case the most significant word
|
||||
if optim_type.isInt:
|
||||
first_x = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
first_y = quote do:
|
||||
cast[`optim_type`](`y`)
|
||||
else:
|
||||
first_x = getAST(most_significant_word(x))
|
||||
first_y = getAST(most_significant_word(y))
|
||||
|
||||
replacing.add first_x
|
||||
replacing.add first_y
|
||||
|
||||
let firstReplacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
# Reset the replacement array
|
||||
replacing = nnkBracket.newTree
|
||||
|
||||
# Setup the loop variables
|
||||
next_x = ident("x_asSignedWordsRawZip")
|
||||
next_y = ident("y_asSignedWordsRawZip")
|
||||
|
||||
# We replace the inner loop with the next_x[idx]
|
||||
replacing.add quote do:
|
||||
`next_x`[`idx`]
|
||||
replacing.add quote do:
|
||||
`next_y`[`idx`]
|
||||
|
||||
let nextReplacedAST = replaceNodes(loopBody, replacing, to_replace)
|
||||
|
||||
# Result:
|
||||
result = newStmtList()
|
||||
result.add firstReplacedAST
|
||||
|
||||
if not optim_type.isInt:
|
||||
# if we have multiple iterations to do
|
||||
if system.cpuEndian == bigEndian:
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
|
||||
let
|
||||
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in 1 ..< `next_x`[].len:
|
||||
# We start from the second word
|
||||
`nextReplacedAST`
|
||||
else:
|
||||
# Little-Endian, iteration in reverse
|
||||
result = quote do:
|
||||
{.pragma: restrict, codegenDecl: "$# __restrict__ $#".}
|
||||
let
|
||||
`next_x`{.restrict.} = cast[ptr `optim_type`](`x`.unsafeaddr)
|
||||
`next_y`{.restrict.} = cast[ptr `optim_type`](`y`.unsafeaddr)
|
||||
for `idx` in countdown(`next_x`[].len - 2, 0):
|
||||
# We stop stop at the second to last word
|
||||
`nextReplacedAST`
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import ./datatypes, macros
|
||||
|
||||
proc optim(x: NimNode): NimNode =
|
||||
proc optimUint(x: NimNode): NimNode =
|
||||
let size = getSize(x)
|
||||
|
||||
if size > 64:
|
||||
|
@ -37,22 +37,7 @@ proc isUint(x: NimNode): static[bool] =
|
|||
elif eqIdent(x, "uint8"): true
|
||||
else: false
|
||||
|
||||
macro most_significant_word*(x: IntImpl): untyped =
|
||||
|
||||
let optim_type = optim(x)
|
||||
if optim_type.isUint:
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)
|
||||
else:
|
||||
when system.cpuEndian == littleEndian:
|
||||
let size = getSize(x)
|
||||
let msw_pos = size - 1
|
||||
else:
|
||||
let msw_pos = 0
|
||||
result = quote do:
|
||||
cast[`optim_type`](`x`)[`msw_pos`]
|
||||
|
||||
proc replaceNodes(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNode =
|
||||
proc replaceNodes*(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNode =
|
||||
# Args:
|
||||
# - The full syntax tree
|
||||
# - an array of replacement value
|
||||
|
@ -75,14 +60,16 @@ proc replaceNodes(ast: NimNode, replacing: NimNode, to_replace: NimNode): NimNod
|
|||
return rTree
|
||||
result = inspect(ast)
|
||||
|
||||
macro asWords*[T](n: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro asWords*(n: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over n, as an array of words.
|
||||
## Input:
|
||||
## - n: The Multiprecision int
|
||||
## - body: the operation you want to do on each word of n
|
||||
## If iteration is needed, it is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(n)
|
||||
optim_type = optimUint(n)
|
||||
var
|
||||
inner_n: NimNode
|
||||
to_replace = nnkBracket.newTree
|
||||
|
@ -111,13 +98,16 @@ macro asWords*[T](n: UintImpl[T], ignoreEndianness: static[bool], loopBody: unty
|
|||
else:
|
||||
assert false, "Not implemented"
|
||||
|
||||
macro asWordsZip*[T](x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro asWordsZip*(x, y: UintImpl or IntImpl, ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over x and y, as an array of words.
|
||||
## Input:
|
||||
## - x, y: The multiprecision ints
|
||||
## If iteration is needed, it is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_x, inner_y: NimNode
|
||||
|
@ -170,15 +160,19 @@ macro asWordsZip*[T](x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody
|
|||
for `idx` in countdown(`inner_x`[].len - 1, 0):
|
||||
`replacedAST`
|
||||
|
||||
macro m_asWordsZip*[T](m: var UintImpl[T], x: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## Iteration is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x: NimNode
|
||||
|
@ -232,15 +226,19 @@ macro m_asWordsZip*[T](m: var UintImpl[T], x: UintImpl[T], ignoreEndianness: sta
|
|||
`replacedAST`
|
||||
|
||||
|
||||
macro m_asWordsZip*[T](m: var UintImpl[T], x, y: UintImpl[T], ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
macro m_asWordsZip*[T: UintImpl or IntImpl](m: var T, x, y: T,
|
||||
ignoreEndianness: static[bool], loopBody: untyped): untyped =
|
||||
## Iterates over a mutable int m and x as an array of words.
|
||||
## returning a !! Pointer !! of the proper type to m.
|
||||
## Input:
|
||||
## - m: A mutable array
|
||||
## - x: The multiprecision ints
|
||||
## Iteration is done from low to high, not taking endianness in account
|
||||
## - If endianness should be taken into account for iteratio order.
|
||||
## If yes, iteration is done from most significant word to least significant.
|
||||
## Otherwise it is done in memory layout order.
|
||||
## - loopBody: the operation you want to do on each word of n
|
||||
let
|
||||
optim_type = optim(x)
|
||||
optim_type = optimUint(x)
|
||||
idx = ident("idx_asWordsRawZip")
|
||||
var
|
||||
inner_m, inner_x, inner_y: NimNode
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes, stdlib_bitops, as_words
|
||||
import ./datatypes, stdlib_bitops, as_signed_words
|
||||
export stdlib_bitops
|
||||
|
||||
# We reuse bitops from Nim standard lib, and expand it for multi-precision int.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ./datatypes, ./bithacks, ./as_words,
|
||||
import ./datatypes, ./bithacks, ./as_words, ./as_signed_words,
|
||||
./bithacks
|
||||
|
||||
func isZero*(n: SomeSignedInt): bool {.inline.} =
|
||||
|
@ -22,3 +22,24 @@ func isZero*(n: IntImpl): bool {.inline.} =
|
|||
func isNegative*(n: IntImpl): bool {.inline.} =
|
||||
## Returns true if a number is negative:
|
||||
n.msb.bool
|
||||
|
||||
func `<`*(x, y: IntImpl): bool {.inline.}=
|
||||
# Lower comparison for multi-precision integers
|
||||
asSignedWordsZip(x, y):
|
||||
if x != y:
|
||||
return x < y
|
||||
return false # they're equal
|
||||
|
||||
func `==`*(x, y: UintImpl): bool {.inline.}=
|
||||
# Equal comparison for multi-precision integers
|
||||
asWordsZip(x, y, ignoreEndianness = true):
|
||||
if x != y:
|
||||
return false
|
||||
return true # they're equal
|
||||
|
||||
func `<=`*(x, y: UintImpl): bool {.inline.}=
|
||||
# Lower or equal comparison for multi-precision integers
|
||||
asSignedWordsZip(x, y):
|
||||
if x != y:
|
||||
return x < y
|
||||
return true # they're equal
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import test_endianness,
|
||||
test_comparison,
|
||||
test_bitwise,
|
||||
test_addsub,
|
||||
test_muldiv
|
||||
import test_uint_endianness,
|
||||
test_uint_comparison,
|
||||
test_uint_bitwise,
|
||||
test_uint_addsub,
|
||||
test_uint_muldiv
|
||||
|
|
Loading…
Reference in New Issue