As the analysis of test errors of the new reigster allocator has shown, we have a problem WRT register allocation. This problem isn't new, but as the allocator is more efficiently reusing registers (or reusing them in a different way) it's exposed again.

0) The register allocator isn't to blame, that looks really fine.

We have two kinds of problems one with exceptions and one with continuations. As exceptions are continuations, we could also define it as the same problem. Anyway, the usage of exceptions is a bit different.
Catch blocks are local labels only AFAIK in HLL. Parrot allows currently a Sub too, but that might be bogus.


1) Exceptions (see t/op/gc_14.imc)

We have a typical usage showing the problem like:

  n = x
  ...         # code that can reuse register of n
  newsub eh, .Exception_Handler, catch
  set_eh eh
   # code that migth through
  n = y
  clear_eh
catch:
  print n

Now the register allocator just doesn't know that there exists a control flow graph edge from anywhere in the try block to the catch label. The register of variable n from the first statement is therefore not preserved, as it's reassigned in the try block. So the catch block can get the wrong value (neither x nor y) of n.

A simble solution could look like:

  n = x
  new eh, .Exception_Handler
  set_eh eh, catch
  # code that might through
  n = y
  clear_eh
catch:

with the changed opcode C<set_eh (in PMC, labelconst INT).

This would suffice to make a real branch target out of the catch label and the register allocator should do the right thing. You can test that by inserting "unless $P0, catch" after "set_eh" in the mentioned test.
(there is some code in imcc that specially treats newsub, but the newsub ocpode isn't the branch - basically everything below C<set_eh> may branch to the catch label)


(Did I already mention that implict behavior like using a register or branching is bad)

2) Continuations (t/op/gc_13.imc [Hi Piers])

Again we have a hidden branch done by a Continuation, or better a loop. The control flow of the main program is basically represented by this conventional code fragment:

          arr1=[...]; arr2=[...]
   loop1: x = choose(arr1)

   loop2: y = choose(arr2)
          ...
         failed = fail()
         goto (loop1, loop2)[failed]

except that the gotos are performed by backtracking via the continuations. So we don't have these loop labels and the continuation continues at the next opcode after the invocation of choose() and not at the shown position above.

So again, the register allocator doesn't see that there is a branch, the variable's arr2 register is clobbered, in this case by the fail closure.

As we never know, if a subroutine captures the return continuation and creates such loops like above, we have in the absence of other syntax actually no chance to hold any variable in a register as far as I can see now. We'd have just to force using lexicals for all vars, except for leaf subroutines (that don't call other subs) or subs that just call one sub (they can't create loops).

Another idea is to create edges from all 1..n function calls in a sub to the previos 0..n-1 calls, so that basically all possible loops done via possible continuations are represented in the CFG.

Comments welcome,
leo



Reply via email to