Forums » Programming with the ECS » Intermediate Code »
How is RestoreRegisterState supposed to be used?
Added by Rochus Keller over 1 year ago
I assume that RestoreRegisterState automatically pushes user registers to stack to avoid a shortage of the latter in sub-expressions.
Currently I try to compile the Lua 5.1 VM using ecc. With codegen.c it works for all *.c files. With codegen.cpp though I get RegisterShortage exceptions (e.g. in lcode.c line 49). I made experiments with RestoreRegisterState, but with no success so far; finally I even put a RestoreRegisterState instance at the top of gen_expr, which from my point of view should be a very conservative register management strategy avoiding register shortage with high probability; but I still get the exception. Therefore I conclude that I haven't understood the concept yet. Hints are highly welcome.
I compile with
/home/me/lua_5.1.5_incl_bit_module/*.c -DLUA_CORE -Dluaall_c -I/home/me/lua_5.1.5_incl_bit_module -I/home/me/EiGen/ecc/include
The lua_5.1.5_incl_bit_module code is attached, just in case.
Replies (7)
RE: How is RestoreRegisterState supposed to be used? - Added by Florian Negele over 1 year ago
The RestoreRegisterState class should be used before you push the arguments for a function call, like you do in the ND_FUNCAL case. The reason is not to avoid register shortages but to make sure their values are properly restored after returning from the function which invalidates all registers as stated in the calling convention. What you can do is basically this:
case ND_FUNCALL: {
const RestoreRegisterState restore {*this};
const int stack_slots = push_args(node);
// ...
The constructor of this class automatically pushes all registers onto the stack which have been marked to be automatically saved and allows the register to be reused afterwards. The deconstructor restores the values from the stack.
In order to manage the registers to be saved you can call the SaveRegister and RestoreRegister function pair. As explained in another message, this is necessary in evaluations of expressions that have two or more operands like all binary expressions. You have to save the first operand before evaluating the second, and then restore the first operand before performing the actual computation. This is for example necessary when the second operand calls a function such that the value of the first operand is stored on the stack by the RestoreRegisterState class. If the second operand does not call a function, the register does not actually need to be saved so the stack is left untouched.
RE: How is RestoreRegisterState supposed to be used? - Added by Rochus Keller over 1 year ago
Ok, thanks. I assumed that RestoreRegisterState does automatically save and restore all registers currently in use, without explicit save/restore.
Now I added RestoreRegisterState, SaveRegister and RestoreRegister and codegen.cpp is able to create all cod without register shortage exception.
Unfortunately now I get out of memory exceptions and an assertion violation in cdamd32. The cod files and console output is in the attached ecc_c++_lua_cod.tar.gz.
Here the console output of the three files with issues when running ecsd -c via generate.sh (also included):
generating lapi.cod cdamd32: fatal error: out of memory generating lauxlib.cod cdamd32: amd64generator.cpp:551: SmartOperand ECS::AMD64::Generator::Context::Access(const ECS::Code::Operand&, const ECS::Code::Type&, Index): Assertion `!operand.displacement' failed. Aborted generating lcode.cod cdamd32: fatal error: out of memory
When I generate the cod with codegen.c and also run generate.sh, I get none of these issues; the cod files are in the attached ecc_c_lua_cod.tar.gz.
RE: How is RestoreRegisterState supposed to be used? - Added by Florian Negele over 1 year ago
The following data section in lapi.cod requires a lot of memory and is probably invalid:
.data luaT_typenames
.alignment 4
res 4294967292
RE: How is RestoreRegisterState supposed to be used? - Added by Florian Negele over 1 year ago
The assertion was triggered by a bug in the code generator, see the attached patch. Sorry for the inconveniences and thanks for reporting.
| register.patch (921 Bytes) register.patch |
RE: How is RestoreRegisterState supposed to be used? - Added by Rochus Keller over 1 year ago
Great, thanks. Now the obf are generated without assertion.
Also thanks for the hint concerning the huge res size; apparently chibicc sets var->ty->array_len to 1 and thus var>ty->size to a negative value if an array is declared like []; the variable is also marked as is_tentative in this case; I don't know what this is used for since there is already a declaration under the same name in a different file; for now I just ignore the declaration if is_tentative is set.
Actually my goal is to get a list of undefined symbols by the linker which is then the set of functions of the standard library I have to implement. Currently I get a number of "duplicated section" errors which I have to analyze.
Thanks again for your help.
RE: How is RestoreRegisterState supposed to be used? - Added by Rochus Keller over 1 year ago
The striked through "1 and thus var" should read "minus one ...".
RE: How is RestoreRegisterState supposed to be used? - Added by Florian Negele over 1 year ago
I assumed that RestoreRegisterState does automatically save and restore all registers currently in use, without explicit save/restore.
I considered that for a while and it is indeed possible to do. Now the registers are actually restored by the RestoreRegister function rather than the destructor as erroneuosly stated above. This restores registers only when actually needed rather than being popped and pushed back immediately during consecutive functions calls.