153 lines
4.5 KiB
Nim
153 lines
4.5 KiB
Nim
# Stint
|
|
# 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, "uint64"): true
|
|
elif 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 div 64 - 1
|
|
else:
|
|
let msw_pos = 0
|
|
result = quote do:
|
|
# most significant word must be returned signed for addition/substraction
|
|
# overflow checking
|
|
cast[int](cast[`optim_type`](`x`)[`msw_pos`])
|
|
|
|
macro least_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 = 0
|
|
else:
|
|
let msw_pos = size div 8 - 1
|
|
result = quote do:
|
|
cast[int](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`
|