Hello,
That is pretty cool and I am interested also in that. I have taken a look and I
have some remarks.
If I understood correctly your approach, the marked code is selected at runtime
according to the CPU properties. To be able to run on different architectures,
the general code must be compiled with a basic set of instructions (like,
'x86-64'). Yet, the code add compiler flags depending on the modules imported
(by the way, the 'passL' options are unnecessary here). Witch means, if you use
AVX2 instructions, the C compiler will use these instructions for the general
code and the program will likely crash on a machine without AVX2 instructions
(Yes, I tested it: got a SIGILL).
In C, one can annotate a function with an attribute that change the code
generation for the function alone. But there is as far as I know no clean way
in Nim to add a specific function attribute to the C generated procedure. An
`emit` pragma just before the proc definition should do the trick. It seems to
work...if we don't use the -d:release flag. In release mode, the emitted
attributes are not necessarily just before the proc definition in the C code. I
have no idea why, a Nim dev may have an answer.
I suggest the use of a procedure passed to a macro.
Example (similar of what you posted above):
proc addition(a,b: openArray[float32]): seq[float32] {.simd.} =
result = newSeq[float32](a.len)
for i in countup(0,a.len-1, simd.width div 4):
let av = simd.loadu_ps(unsafeAddr a[i])
let bv = simd.loadu_ps(unsafeAddr b[i])
let rv = simd.add_ps(av,bv)
simd.storeu_ps(addr result[i],rv)
The macro **simd** creates two procs, **additionsse2** and **additionavx2**,
marked by the C attributes (when it works). The true **addition** proc calls
the correct proc at runtime.
Here is the definition of the **simd** macro I used:
macro simd*(procDef:untyped): untyped =
result = newStmtList()
let psse2 = makeSimdProcDef(procDef, "sse2", "128")
let csse2 = makeCallForDispatch(psse2,procDef)
result.add newEmitPragma(attrTarget%"sse2")
result.add psse2
let pavx2 = makeSimdProcDef(procDef, "avx2", "256")
let cavx2 = makeCallForDispatch(pavx2,procDef)
result.add newEmitPragma(attrTarget%"avx2")
result.add pavx2
procDef.body = quote do:
if cpuType == UNINITIALIZED:
cpuType = getCPUType()
echo "Detected cpu type:" & $cpuType
if cpuType == SSE2 or cpuType == SSE41:
`csse2`
elif cpuType == AVX2 or cpuType == AVX:
`cavx2`
result.add procDef
#echo repr(result)
I skipped some helper functions for the sake of brevity, but I will give you
the entire thing if you are interested.
Sorry about the too long post, I hoped it helped you a bit despite the issues I
raised.