Em seg, 10 de fev de 2020 19:29, Paul Cercueil <address@hidden
Le lun., févr. 10, 2020 at 18:56, Paulo César Pereira de Andrade
<address@hidden> a écrit :
> Em seg., 10 de fev. de 2020 às 17:43, Paul Cercueil
> <address@hidden> escreveu:
>> Hi Paulo,
> Hi Paul,
>> Le lun., févr. 10, 2020 at 17:33, Paulo César Pereira de Andrade
>> <address@hidden> a écrit :
>> > Em seg., 10 de fev. de 2020 às 15:09, Paulo César Pereira de
>> > <address@hidden> escreveu:
>> > [...]
>> >> > Here is a new example program that shows incorrect behaviour
>> >> the
>> >> > latest master.
>> >> > My JIT_R1 (== r10) is set at the beginning of the program and
>> >> read back
>> >> > at the end, but it's used in between as a temporary register
>> >> > Lightning.
>> >> With the correction of the bug in git master, the example
>> >> be
>> >> correct.
>> >> Checking only on for now Linux it did not reuse the register,
>> >> if
>> >> you are sure the functions called with jit_callr(JIT_R2) do not
>> >> modify
>> >> JIT_R1, it is required to add a jit_live(JIT_R1) because
>> >> non
>> >> callee save registers are considered dead when returning from a
>> >> function.
>> > You can use jit_live(JIT_R1) if *really* 100% certain the
>> > calls
>> > do not clobber it. Otherwise, another approach would be to save
>> to the
>> > stack and reload when returning from the function. You cannot
>> > guarantee
>> > the called function will not modify JIT_R1, because even if it is
>> > jit
>> > function, it might be used as a temporary there.
>> Yes, that's what I do - the called functions are trampolines that
>> C calls with code to save/restore all caller-saved registers, so it
>> guaranteed that they won't be modified.
>> The problem is not in the called function, though. As you can see I
>> call jit_live(JIT_R1), but JIT_R1 gets used as temporary in the
>> generated code anyway. The reason why it happens on Windows and not
>> Linux is because of the different ABI, which means jit_get_reg() is
>> used much more often there.
> I understand what is happening. Sorry for the confusion.
> I am afraid for this condition it would be required some new code,
> only jit_live() abstraction is not enough.
> The reason is that jit_live() is only a hint. And it is considered
> after that point, or, if there is no function call or jump to unknown
> location it will propagate to earlier code.
> In this case, it is used in a range where it thinks the value is
> due to the function calls.
> Basically, in your example, jit_live() is being used as a hint to
> that the function did return a value in JIT_R1.
Alright, so would it be enough to call jit_live() on all JIT_Rx
registers *before* and after function calls?
To not modify jit_live() semantics this would be the best option.
A new primitive could be added for complex cases. But really, should use JIT_Vx or manually spill/reload.
Attempting to explain the issue. jit_live() is an instruction to tell the register is live at the moment it, jit_live, is called.
From that moment the register is marked live. It is also marked live backwards, considering blocks; it marks as live at the start of backward blocks, and (start of) forward blocks (there is recursion involved, implemented sequentially, not just a jump target) if it sees an use as argument. What cause issues with JIT_Rx, sans possibly not yet known bugs, is that at a function call it is marked dead, as well as at a function return, implicit, as JIT_Rx are not callee save registers.
In other words, using JIT_Rx around function calls or jumps to unknown, or runtime resolved targets may lead do bugs. The problem you experienced is that it was considered dead, and used as a temporary between the point It was set to a value, kind of dead code for the algorithm, and a function call. Effectivelly your code told it was live at function return, like a secondary return value. This is why the need to tell it is live before and after the function call.
There is a long standing TODO to create an abstraction where JIT_xx could be considered as variables. Right now you must see them as either calee or callee save values. And understand that jumps to non resolvable at jit code generation time (not constant or not to a jit_label) targets are treated as function calls.