Hello, 

I have found an interesting bug when increasing the pressure on the register
allocator after some modifications in use-def (namely marking MUL as defining
EAX and EDX on x86).

I have an interval (actually, a split-out part of an interval) that gets no
register allocated.

Here is the regalloc trace :

Liveness:

      0  1  2  3  4  5  6  7  8  9  10 11 12 13 14
  14:                               --------------- (start: 10, end: 15)
  13:                               --------------- (start: 10, end: 15)
  12:             ********************************* (start:  4, end: 15)
  11:                   *************************** (start:  6, end: 15)
  10:                               --------------- (start: 10, end: 15)
   9:                            ------------------ (start:  9, end: 15)
   8:          ************************************ (start:  3, end: 15)
   7:       *************************************** (start:  2, end: 15)
   6:    ****************************************** (start:  1, end: 15)
   5: ********************************************* (start:  0, end: 15)
   4:                               --------------- (start: 10, end: 15)
   3:                                               (empty)
   2:                               --------------- (start: 10, end: 15)
   1:                                               (empty)
   0:                                               (empty)

found with i=0
Register Allocation:

  14 (pos: 10-15):      EDX     fixed           no spill        no reload
  13 (pos: 10-15):      EAX     fixed           no spill        no reload
  12 (pos:  4-10):      EDX     non-fixed       spill           no reload
  12 (pos: 10-15):      EBX     non-fixed       no spill        reload
  11 (pos:  6- 9):      EAX     non-fixed       spill           no reload
  11 (pos:  9-15):      <unassigned>    non-fixed       no spill        reload
  10 (pos: 10-15):      EDX     fixed           no spill        no reload
   9 (pos:  9-15):      EAX     fixed           no spill        no reload
   8 (pos:  3-15):      EDI     non-fixed       no spill        no reload
   7 (pos:  2-15):      ESI     non-fixed       no spill        no reload
   6 (pos:  1-10):      EBX     non-fixed       spill           no reload
   6 (pos: 10-15):      EBX     non-fixed       no spill        no reload
   5 (pos:  0-15):      ECX     non-fixed       no spill        no reload
   4 (pos: 10-15):      EDX     fixed           no spill        no reload
   3 (pos: -1- 0):      ECX     fixed           no spill        no reload
   2 (pos: 10-15):      EAX     fixed           no spill        no reload
   1 (pos: -1- 0):      ESP     fixed           no spill        no reload
   0 (pos: -1- 0):      EBP     fixed           no spill        no reload

As you can see, interval 11 is split, the first part correctly marked as
spilled at the end, the second part correctly marked as reloaded in the
beginning, but the second part gets no register allocated.

How is this possible? Whenever we split an interval (reminder: we implement
Wimmer's thesis, 2004 algorithm), we add the split child to the unhandled list,
which is sorted by ascending start position.

And here comes the interesting part: the algorithm relies on browsing this
unhandled list. We do it using  "list_for_each_entry_safe(current, tmp,
&unhandled, interval_node)".

_safe is safe for deletion of the current list element - it does this by
preloading the next element before iterating on the current one. One side
effect is that if the iteration on the current element modifies its successor,
the _safe iterator will *not* properly pick it up.

As a result our interval is not considered by the register allocator, which is
I believe why it gets no register allocated.

How did I prove that the successor was getting modified (if you modify elements
further away in the list everything goes fine)? 

Here is a piece of patch that traces the position of insertion into the list
when spilling an interval:

 /* Inserts to a list of intervals sorted by
increasing start position. */ +int outp = 0;
 static void
 insert_to_list(struct live_interval *interval, struct list_head *interval_list)
 {
@@ -110,11 +111,15 @@ insert_to_list(struct live_interval *interval, struct
list_head *interval_list)
         * If we find an existing interval, that starts _after_ the
         * new interval, add ours before that.
         */
+       int i = 0;
        list_for_each_entry(this, interval_list, interval_node) {
                if (interval->range.start < this->range.start) {
                        list_add_tail(&interval->interval_node,
&this->interval_node);
+                       if(outp)
+                               printf("found with i=%d\n",i);
                        return;
                }
+               i++;
        }

        /*
@@ -298,7 +303,9 @@ static void try_to_allocate_free_reg(struct live_interval
*current, */
                new = split_interval_at(current, free_until_pos[reg]);
                mark_need_reload(new, current);
+               outp=1;
                insert_to_list(new, unhandled);
+               outp=0;
                current->reg = reg;
                current->need_spill = 1;
        }


The output I get?

"found with i=0"

The solution I have in mind is to avoid using the _safe iterator, but simply
pick the head of the unhandled list at every iteration.

I am sending this explanation because it is important so my next patch is well
understood.

-- 
Greetings, 
A.H.

------------------------------------------------------------------------------
This SF.net email is sponsored by:
High Quality Requirements in a Collaborative Environment.
Download a free trial of Rational Requirements Composer Now!
http://p.sf.net/sfu/www-ibm-com
_______________________________________________
Jatovm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jatovm-devel

Reply via email to