nim-stint/src/private/bithacks.nim

68 lines
2.7 KiB
Nim
Raw Normal View History

2018-03-02 10:48:08 +00:00
# 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 ./uint_type, stdlib_bitops, size_mpuintimpl
2018-03-26 12:46:38 +00:00
# We reuse bitops from Nim standard lib and optimize it further on x86.
# On x86 clz it is implemented as bitscanreverse then xor and we need to again xor/sub.
# We need the bsr instructions so we xor again hoping for the compiler to only keep 1.
proc bit_length*(x: SomeInteger): int {.noSideEffect.}=
when nimvm:
when sizeof(x) <= 4: result = if x == 0: 0 else: fastlog2_nim(x.uint32)
else: result = if x == 0: 0 else: fastlog2_nim(x.uint64)
else:
when useGCC_builtins:
when sizeof(x) <= 4: result = if x == 0: 0 else: builtin_clz(x.uint32) xor 31.cint
else: result = if x == 0: 0 else: builtin_clzll(x.uint64) xor 63.cint
elif useVCC_builtins:
when sizeof(x) <= 4:
result = if x == 0: 0 else: vcc_scan_impl(bitScanReverse, x.culong)
elif arch64:
result = if x == 0: 0 else: vcc_scan_impl(bitScanReverse64, x.uint64)
else:
result = if x == 0: 0 else: fastlog2_nim(x.uint64)
elif useICC_builtins:
when sizeof(x) <= 4:
result = if x == 0: 0 else: icc_scan_impl(bitScanReverse, x.uint32)
elif arch64:
result = if x == 0: 0 else: icc_scan_impl(bitScanReverse64, x.uint64)
else:
result = if x == 0: 0 else: fastlog2_nim(x.uint64)
else:
when sizeof(x) <= 4:
result = if x == 0: 0 else: fastlog2_nim(x.uint32)
else:
result = if x == 0: 0 else: fastlog2_nim(x.uint64)
proc bit_length*(n: MpUintImpl): int {.noSideEffect.}=
## Calculates how many bits are necessary to represent the number
const maxHalfRepr = n.lo.type.sizeof * 8 - 1
2018-02-17 16:57:26 +00:00
# Changing the following to an if expression somehow transform the whole ASM to 5 branches
# instead of the 4 expected (with the inline ASM from bit_length_impl)
# Also there does not seems to be a way to generate a conditional mov
2018-03-26 12:46:38 +00:00
let hi_bitlen = n.hi.bit_length
result = if hi_bitlen == 0: n.lo.bit_length
else: hi_bitlen + maxHalfRepr
proc countLeadingZeroBits*(x: MpUintImpl): int {.inline, nosideeffect.} =
## Returns the number of leading zero bits in integer.
const maxHalfRepr = size_mpuintimpl(x) div 2
let hi_clz = x.hi.countLeadingZeroBits
result = if hi_clz == maxHalfRepr:
x.lo.countLeadingZeroBits + maxHalfRepr
else: hi_clz