From e2ab2dbf598818655aa23737274f630103644586 Mon Sep 17 00:00:00 2001 From: mratsim Date: Wed, 25 Apr 2018 17:02:44 +0200 Subject: [PATCH] Add signed word iteration --- src/private/as_signed_words.nim | 134 ++++++++++++++++++ src/private/as_words.nim | 58 ++++---- src/private/bithacks.nim | 2 +- src/private/int_comparison.nim | 23 ++- tests/all_tests.nim | 10 +- .../{test_addsub.nim => test_uint_addsub.nim} | 0 ...test_bitwise.nim => test_uint_bitwise.nim} | 0 ...omparison.nim => test_uint_comparison.nim} | 0 ...ndianness.nim => test_uint_endianness.nim} | 0 .../{test_muldiv.nim => test_uint_muldiv.nim} | 0 10 files changed, 190 insertions(+), 37 deletions(-) create mode 100644 src/private/as_signed_words.nim rename tests/{test_addsub.nim => test_uint_addsub.nim} (100%) rename tests/{test_bitwise.nim => test_uint_bitwise.nim} (100%) rename tests/{test_comparison.nim => test_uint_comparison.nim} (100%) rename tests/{test_endianness.nim => test_uint_endianness.nim} (100%) rename tests/{test_muldiv.nim => test_uint_muldiv.nim} (100%) diff --git a/src/private/as_signed_words.nim b/src/private/as_signed_words.nim new file mode 100644 index 0000000..fd8e973 --- /dev/null +++ b/src/private/as_signed_words.nim @@ -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` diff --git a/src/private/as_words.nim b/src/private/as_words.nim index 599fc93..778bc61 100644 --- a/src/private/as_words.nim +++ b/src/private/as_words.nim @@ -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 diff --git a/src/private/bithacks.nim b/src/private/bithacks.nim index ad33a92..a6d6d01 100644 --- a/src/private/bithacks.nim +++ b/src/private/bithacks.nim @@ -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. diff --git a/src/private/int_comparison.nim b/src/private/int_comparison.nim index d747a06..3092c08 100644 --- a/src/private/int_comparison.nim +++ b/src/private/int_comparison.nim @@ -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 diff --git a/tests/all_tests.nim b/tests/all_tests.nim index bf8fa19..9de5f21 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -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 diff --git a/tests/test_addsub.nim b/tests/test_uint_addsub.nim similarity index 100% rename from tests/test_addsub.nim rename to tests/test_uint_addsub.nim diff --git a/tests/test_bitwise.nim b/tests/test_uint_bitwise.nim similarity index 100% rename from tests/test_bitwise.nim rename to tests/test_uint_bitwise.nim diff --git a/tests/test_comparison.nim b/tests/test_uint_comparison.nim similarity index 100% rename from tests/test_comparison.nim rename to tests/test_uint_comparison.nim diff --git a/tests/test_endianness.nim b/tests/test_uint_endianness.nim similarity index 100% rename from tests/test_endianness.nim rename to tests/test_uint_endianness.nim diff --git a/tests/test_muldiv.nim b/tests/test_uint_muldiv.nim similarity index 100% rename from tests/test_muldiv.nim rename to tests/test_uint_muldiv.nim