I've identified one cause of BusError on startup. (I'm not entirely sure
it is the only one, but it definitely is the cause for the easily
reproduceable one, and while I think I saw one with this worked around
in the early tests, I cannot reproduce the other, so there might either
be another issue left or I once forget to save or recompile).

The cause is corruption at the following piece of code in igcref.c:715:

715:            ref rtemp;
716:
717:            if_debug2('8', "  [8]ref 0x%lx copied to 0x%lx\n",
718:                      (ulong) src, (ulong) dest);
719:            /* We can't just use ref_assign_inline, */
720:            /* because the source and destination */
721:            /* might overlap! */
722:            ref_assign_inline(&rtemp, (ref *) src);
723:            r_clear_attrs(&rtemp, l_mark);
724:            ref_assign_inline((ref *) dest, &rtemp);

As far as I understand, the problem is that gcc knows that two (ref*)
pointers cannot possibly overlap, so it optimizes away the temporary
variable (at least partially), causing corruption of the packed array.
(Which then again causes bus errors in other code like the garbage collection).

Here is the assember generated by gcc with -O2:
        .loc 1 722 0
        ld      [%g3+4], %g2
        .loc 1 724 0
        st      %g2, [%i1+4]
        sth     %g1, [%i1]
        .loc 1 722 0
        st      %g2, [%fp-20]
        lduh    [%g3+2], %g1
        .loc 1 725 0
        add     %g3, 8, %g3
        .loc 1 724 0
        sth     %g1, [%i1+2]

It first does part of the move from src to register, then from register to dest,
only then copying another part of src to register, clearing the bit and storing
that part to dest. As the clearing is done in the part with the lower address,
it even copies the second part first, thus if compressing an array quite likely
overwriting a part it just reads after that.

The attached patch fixes it for me, and I think it also is the correct solution
that will survive more inteligent compilers.

Hochachtungsvoll,
        Bernhard R. Link
--- src/igcref.c	2007-11-30 07:43:12.000000000 +0100
+++ src/igcref.c	2008-05-11 10:58:50.000000000 +0200
@@ -712,16 +712,17 @@
 	    src++;
 	} else {		/* full-size ref */
 	    if (r_has_attr((ref *) src, l_mark)) {
-		ref rtemp;
-
 		if_debug2('8', "  [8]ref 0x%lx copied to 0x%lx\n",
 			  (ulong) src, (ulong) dest);
 		/* We can't just use ref_assign_inline, */
 		/* because the source and destination */
-		/* might overlap! */
-		ref_assign_inline(&rtemp, (ref *) src);
-		r_clear_attrs(&rtemp, l_mark);
-		ref_assign_inline((ref *) dest, &rtemp);
+		/* might overlap! A temp variable does not */
+		/* help either as long as ref_assign* are used, */
+		/* as that needs casting to (ref*) which is not */
+		/* allowed to overlap so the compiler might */
+		/* (and sometimes actually does) reorder it */
+		memmove(dest, src, sizeof(ref));
+		r_clear_attrs((ref *)dest, l_mark);
 		src += packed_per_ref;
 		dest += packed_per_ref;
 	    } else {		/* check for end of block */

Reply via email to