TCC follows the native calling convention, i.e. msvc on windows and ELF
psABI on linux.
Thank you for answering my question thoroughly.
> 4) If I did something weird like have a call out from generated code to my
> code, and my code returned on the same stack but in the context of a
> different thread than it entered from, would that break anything?
No. Or, perhaps better said, it would break in the same way when the
generated code would also be your code and not generated by TCC, i.e. TCC
doesn't introduce additional restrictions. In particular the usual
makecontext/swapcontext way of implementing lightweight threads via stack
switching should work just fine, as should any more unusual way of
switching threads but not stack (what is that even supposed to mean?), as
long as the input code doesn't have any problem if it had been written
literally without TCC involvement.
"switching threads but not stack (what is that even supposed to mean?)"
What it means is this, imagine that a scheduler outside the generated code created a new stack, switched to it then called some generated code.
And at some point that generated code rather than returning to the scheduler yielded by making a call into my own code which, instead of returning, just saved the context/continuation somewhere and switched back to its native stack.
Then later on, a scheduler running on a different thread with a different thread local state, for instance, takes that stack and switches to it and returns into the generated code, appearing to
return from the call, but in a different thread.
Any code that used thread local memory might break.
Tinycc-devel mailing list
I assume that tcc_compile_string is equivalent to tcc_add_file. Does that mean that you can add multiple strings to be compiled? Does tcc copy them, or does it compile them immediately and forget them, or do the original buffers have to be retained?
Kyryl's answers brought up a bunch more questions for me.
For instance, he said "tcc_delete will free everything, if you call it before running generated code it will crash."
So I wonder,
1) when is the run time library loaded, when is it initialized and is it ever freed or finalized?
If a jit made a different TCCState for each routine it compiles, say 1000 routines,
a) would tcclib load 1000 copies of the runtime library?
b) would it make a static variable section for the runtime library 1000 times and initialize the variables in it?
c) would it make a different heap for each routine? If I call malloc in one routine and then free it in another routine would it try to free it into a different heap and corrupt a heap?
d) if the jit wanted to update a routine with different code, so it called tcc_delete on that code's state, then made a new state and compiled new code for it, would that break anything? Would tcc_delete finalize anything? Delete a heap?
ie., is tcclib really designed to be used the way a jit would use it?
He also said that it's possible to reuse a TCCState but "Yes you can reuse it, but it's not going to be efficient because it
will have to recompile the previous stuff also."
a) does it remember that I called tcc_set_output_type, tcc_add_library_path, tcc_add_symbol, tcc_add_file, or tcc_compile_string? If it remembers tcc_compile_string, did it save a copy of the string?
3 And since he said I'd have to recompile the previous things, what happened to the previously compiled code?
b) is the problem one I asked about before, Question 1's run time libraries, heaps or or Question 2's saving
tcc_compile_string from last time?
It sure would be cool if it turns out that libtcc loaded and initialized its runtime library before any TCCState is made, and that runtime library is shared between all states. That would be what people who make jits want.
But I'm still worried by the answer that you can't run code after a TCCState is deleted.