Use compiler-specific functions in Nim

C compilers, like GCC, provide various "builtin" compiler-specific extensions (functions). Sure, using them may be looked down upon, but sometimes they can give you access to hardware-optimized operations that significantly boost performance. You can always provide a slower fallback.

Let's take as an example the operation "find how many bytes a number would take up". I needed this to implement a function that returns a random number in a specific range, using an entropy source that just gives bytes, such as /dev/urandom.

proc byteSizeFallback(n: uint64): int =
  ## Returns the smallest number `b` that `256^b <= n`
  var n = n
  while true:
    inc result
    n = n shr 8
    # byteSizeFallback(0) == 1
    if n == 0:

GCC provides a function __builtin_clz:

Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.

For example, the number 00011001 (when talking in 8-bit context) has 3 leading 0-bits, so it takes up 8-3 == 5 bits. Let's use __builtin_clzll to do the same thing for a 64 bit number, then just use the "bit size" to get the "byte size".

when defined(gcc):
  proc gccClz(n: culonglong): cint =
    {.cdecl, importc: "__builtin_clzll".}

  proc bitSize(n: uint): int {.inline.} =
    sizeof(culonglong)*8 - gccClz(n)

  proc byteSize*(n: uint): int {.inline.} =
    (bitSize(n)+7) div 8  # instead of ceil(bitSize(n)/8)

Now let's do this for Visual C, using _BitScanReverse. Credits to this StackOverflow answer.

Untested code:

elif defined(vcc):
  proc vcBitScanReverse(index: var culong, mask: int64): cuchar
    {.cdecl, importc: "_BitScanReverse64".}

  proc bitSize(n: uint): int {.inline.} =
    var result: culong
    if vcBitScanReverse(result, n):
      31 - result
      return 32

  proc byteSize*(n: uint): int {.inline.} =
    (bitSize(n)+7) div 8  # instead of ceil(bitSize(n)/8)

And the fallback...

  proc byteSize*(n: uint): int {.inline.} =

A list of compilers can be found here.

Comments powered by Disqus