nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.59k stars 1.47k forks source link

global assignments from {.compileTime.} functions should codegen to c initializers #6947

Open RedBeard0531 opened 6 years ago

RedBeard0531 commented 6 years ago

Minimal example code taken from https://github.com/RedBeard0531/advent_of_code_2017/blob/master/16/a16_2_sse_brute.nim:

# Build the tables used for pshufb instructions.
proc makeShuffles: array[16, array[16, byte]] {.compileTime.}=
  for i in 0..<16:
    for j in 0..<16:
      result[i][j] = ((j + 16 - i) mod 16).byte
let shuffles = makeShuffles()

This codegens to a bunch of single byte assignments in NimMainModule. Ideally this would codegen to something more like:

typedef NU8 tyArray1[16];
typedef tyArray1 tyArray2[16];
const tyArray2 shuffles = {
  {0,1,2 /*...*/},
  {1,2,3 /*...*/),
  /*...*/
};

This would cause the compiler to put shuffles in the .rodata section of the executable so there will be no run-time cost of building the table.

jangko commented 6 years ago

I don't think this is a bug. perhaps a documentation issue. If you use 'const' instead of 'let', the compiler will produce expected code. 'let' guarantee that the compiler will produce a location(it still can be mutated via unsafeAddr), but not always(perhaps never) evaluated at compile time. In your example, the compiler choose to unroll the loop and inline the proc on the other hand, 'const' guarantee it is evaluated at compile time but not always generate a location. actually, Nim manual already describe this with different words. both 'let' and 'const' section will be optimized by the compiler differently.

RedBeard0531 commented 6 years ago

@jangko I'd love to be able to use const here. The problem is that you can't take the address of a const at runtime, even with unsafeAddr. This makes it unusable for (some kinds of) data tables, especially those used for simd operations because the simd registers are loaded from an address, and currently the only way to do that in nim is via an importc compiler intrinsic.

Also, I think mutating a let through unsafeAddr is considered an unchecked programmer error (aka UB). It is not something that the compiler needs to be able to generate code that can tolerate.