Hello there.
I'm reading about Nim to start a research project but couldn't find how to do exactly what I need. Yes, I'm new to Nim and have only tested the Hello world code online but am looking forward to use it more seriously.
Basically, I wanna pass a string containing the source code of a C function to some api and be able to call the compiled code. I found that compileAPI and hcr exist, but couldn't find any examples using C. If they exist, please accept my apologies. There's also dynlib but (if I'm not mistaken), that requires me to save the code to a file first and I was hopping everything could be done in-memory.
I'm focusing on using tcc first because it's fast and I need the code compilation to happen as fast as possible, hence why I'm using C for the function to avoid any extra step translating Nim to C. However, as the problem I'm trying to solve gets larger, I'll need a compiler that can do optimization; that's why something like nlvm could be another option and then writing the code in Nim could work.
As an example of a source code, suppose a very simple function like this (ignore any typos or mistakes for now, just focus on the idea :-) ). Both mat, G, and result would be created in Nim:
void calc(float ** mat, int N, int M, struct graph *G, float * result) {
int i, j;
for (i=0; i<N; i++)
for (j=0; j<M; j++)
result += mat[i][j] * G[i*j + j]->weight;
}
Thanks.
Basically, I wanna pass a string containing the source code of a C function to some api and be able to call the compiled code. I found that compileAPI and hcr exist, but couldn't find any examples using C.
You can inline C code in Nim with the {.emit: myCcode.} pragma (https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma) so if you get HCR running for Nim you can have the proper C code.
Now, the whole context is very confusing and doesn't really make sense to me.
the source code of a C function to some api
If you have a C API, it's somewhat stable, why would you need to dynamically recompile your API calls?
I'm focusing on using tcc first because it's fast and I need the code compilation to happen as fast as possible
I don't understand why you need to compile something in the first place, either it's an API and it's stable or it's not and if you need fast self-modifying code, use an interpreted language, bytecode VM or a JIT to skip the compilation step.
« hence why » is always grammatically inappropriate. « Hence » already means « that is the reason why ». While we do not want to constrain others with strict grammar rules on this forum, I would advise checking your grammar (potentially with bots like Grammarly) and making your future texts as concise as possible. Working on your speech is half the work done towards solving your problem.
Are you looking for information on the foreign function interface?
From what I understand, you want to :
1. Quickly compile functions in Nim to C with TCC (the Tiny C Compiler?). Why do you need compilation to be quick? Fast prototyping?
To get both advantages, you decided to store the (Nim compiled) C code of certain functions and inject this C code in the C output of the rest of the Nim code. Now, you can not use a shared/dynamic C library because you want the ability to modify the functions from Nim itself by string manipulation. That is what I understood.
I still do not get your intent with your calc function.
Thanks @mratsim and @dlesnoff for your kind replies. I'll try to make it more clear and direct. I hope it's better now and I'm happy clarify it more if needed.
I'm looking for a way to compile and call a C source code passed as a string. It has to be compiled while the program is running because the C code will keep changing (it's dynamically created by a C code generator). For instance:
* Load data
* while True:
- Generate a new C function
- Compile the C function
- Call the C function passing the data
- Evaluate the result
- Do something else ...
I'll eventually have many C functions that have to run in parallel or distributed in a cluster but that's not a concern at the moment.
I don't understand why you need to compile something in the first place, either it's an API and it's stable or it's not and if you need fast self-modifying code, use an interpreted language, bytecode VM or a JIT to skip the compilation step.
Yes, it's self-modifying code. I currenly have a Python code but I want something that runs much faster. That's why I'm planning on moving to Nim/C. Because it's a prototype, I don't wanna spend time implementing a VM or JIT.
From what I understand, you want to : 1. Quickly compile functions in Nim to C with TCC (the Tiny C Compiler?). Why do you need compilation to be quick? Fast prototyping?
I want to call TCC to compile a C code that's in a string and be able to call the compiled function without having to recompile the whole program. I might need to generate and run thousands of C functions.
As inputs to your function scale, use a more optimizing slower compiler. To get both advantages, you decided to store the (Nim compiled) C code of certain functions and inject this C code in the C output of the rest of the Nim code. Now, you can not use a shared/dynamic C library because you want the ability to modify the functions from Nim itself by string manipulation.
As inputs scale, I wanna use a more optimizing slower compiler. I want compilation to happen in-memory to avoid wasting time persisting files.
I still do not get your intent with your calc function.
Assume that the calc function receives inputs, does some calculations, and stores the outputs in the result array that will be evaluated in the Nim code. The calculation I showed in the example is not what I have to implement. It can be, for instance, a system of differential equations.
Thanks again.
Cling (LLVM-based) does something like you described.
See the demo. In particular see the function useSymbolAddress
I realized you can run C programs in memory with TinyCC (which is much thiner than Clang/LLVM but the generated code is less optimal)
Setup:
git clone https://github.com/TinyCC/tinycc
cd tinycc
./configure
make
cd ../
See example here (tcc_run.nim, tcc.nim wraps tcc lib): https://gist.github.com/guibar64/5a5a028587fe961bc54816de7007006d
Hey @guibar.
They might be exactly what I was looking for. Don’t know about the performance of the cling interpreter compared to the code compiled with clang but I’ll give it a try.
Thanks a lot!
dynamically created by a C code generator
So there's something somewhere that generates C code using full spectrum of C semantics? What does it generate the code from? Is the generated code really unrestricted in any way?
Can you give me a bigger-picture description of why you are wanting to do this? I suspect you will get much better responses.
But, for fun, I'll take a crack at this without that knowledge. I'd think a event oriented system tied to a kubernetes cluster would work best. Works like this:
A static API written in any language responds to POST queries by throwing messages into RabbitMQ server cluster "inbound" exchange. The message contains an ID and a long string containing the C code "job". A secondary Rabbit consumer (written in Python with Celery?), consumes the queue pointing at the exchange for each job. The Python uses kubectl to spawn the instance of a new POD in a k8s cluster. The virtual POD is based on Alpine, Nim, etc and the Dockerfile references the C code and the batch code. The POD compiles and executes the code streaming the results to STDOUT. A sidecar in the POD captures the result (or compiler error messages etc) and sends the results back as a message to a RabbitMQ "results" exchange before despawning itself. This sidecar can also monitor for hung loops and can kill the POD if needed (again, sending a message first.)
This "result" exchange's Python consumer can either save results into a DB readable by the API. Or can do "Do something else .." as described above.
This will allow for a self-leveling very-high-volume of code snippets being run and evaluated. For less than $1000/month on AWS or Digital Ocean; you could run through a mind-boggling amount of code.
Note: don't expose the API to the Internet. Hackers would have a joyous field day with this.
Hello @guibar.
I tested the tcc gist you shared but it's failing. I'm compiling with nim 1.6.12, tcc 0.9.27, gcc 9.4.0:
nim c -d:danger --threads:on --cincludes:/content/tcc_nim/tinycc/include tcc_run.nim
Error message:
/usr/bin/ld: /root/.cache/nim/tcc_run_r/libtcc.c.o: in function `rt_error': libtcc.c:(.text+0x17fc): undefined reference to `__va_start' /usr/bin/ld: /root/.cache/nim/tcc_run_r/libtcc.c.o: in function `error1': libtcc.c:(.text+0x1b17): undefined reference to `__va_start' /usr/bin/ld: libtcc.c:(.text+0x1b9f): undefined reference to `__va_start' /usr/bin/ld: libtcc.c:(.text+0x1c1a): undefined reference to `__va_start' /usr/bin/ld: libtcc.c:(.text+0x1d39): undefined reference to `__va_start' /usr/bin/ld: /root/.cache/nim/tcc_run_r/libtcc.c.o:libtcc.c:(.text+0x1db4): more undefined references to `__va_start' follow collect2: error: ld returned 1 exit status Error: execution of an external program failed: 'gcc -o /content/tcc_nim/tcc_run /root/.cache/nim/tcc_run_r/libtcc.c.o /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@sstd@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@spure@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@[email protected] /root/.cache/nim/tcc_run_r/@m..@s..@[email protected]@[email protected]@slib@[email protected] /root/.cache/nim/tcc_run_r/@mtcc_run.nim.c.o -pthread -lm -lm -lrt -ldl' :: Reference to `__va_start' is in stdarg.h which I also added in the cincludes argument. I get the same error without cincludes. Any suggestion to solve this? Thanks
So there's something somewhere that generates C code using full spectrum of C semantics? What does it generate the code from? Is the generated code really unrestricted in any way?
Not full spectrum. Just a tiny subset to solve the problems we have. The code has some restrictions to avoid underflows, overflows, out-of-bounds, conflicting types.
By the way, mind sharing the command-line you're using to compile the code you shared? Maybe I'm missing something there.
Thanks
I got it working, but it feels hacky.
Anyway, thanks everyone again for the help.
I'm focusing on using tcc first because it's fast and I need the code compilation to happen as fast as possible, hence why I'm using C for the function to avoid any extra step translating Nim to C. However, as the problem I'm trying to solve gets larger, I'll need a compiler that can do optimization; that's why something like nlvm could be another option and then writing the code in Nim could work.
Since you already got plenty of useful advice, here is my unhelpful one: Just write your program in Nim from the start and enjoy subscript checking, automatic memory management, libraries that can do matrix multiplications... The compile times won't be an issue.