BC ABI: handling volatile

David Daney ddaney@avtrex.com
Wed Jun 14 15:55:00 GMT 2006


Andrew Haley wrote:
> We don't properly implement volatile when compiling BC, and as I see
> it there's no way to do so without extending the ABI.
> 
> [I'm going to talk about non-static fields here, but the same argument
> applies to static fields.]
> 
> When accessing fields in another class, we generate something like
> 
> 
>     D.678 = _otable_p[1];
>     if (D.678 == 0)
>       {
>         _Jv_ThrowNoSuchFieldError (1);
>       }
>     else
>       {
>         D.679 = _otable_p[1];
>         iftmp.2 = (<unnamed type>) D.679;
>       }
>     D.680 = iftmp.1 + iftmp.2;
>     D.681 = *D.680;
> 
> which in turn generates code like
> 
>         movl    _otable_p+4, %eax       # _otable_p, D.705
>         testl   %eax, %eax      # D.705
>         je      .L11    #,
>         movl    (%edx,%eax), %eax       #* D.705, tmp64
> 
> ...
> 
> .L11:
>         movl    $1, (%esp)      #,
>         call    _Jv_ThrowNoSuchFieldError       #
> 
> This isn't all that bad, being only a single inline conditional
> branch between the otable fetch and the load.  
> 
> However, volatile present an additional problems: accessing a volatile
> field has side efects.
> 
> We need to insert memory barriers when accessing volatiles (on x86
> that's lfence after a load and mfence before a store.)  Also, at the
> time of compilation we can't know whether a field is declared
> volatile.  So, we could do something like this:
> 
>   D_678 = _otable_p[1];
>   iftmp_2 = D_678;
> 
>   if (D_678 <= 0)
>     {
>       if (D_678 == 0)
> 	{
> 	  _Jv_ThrowNoSuchFieldError (1);
> 	}
>       else
> 	{
> 	  iftmp_2 = -iftmp_2;
> 	  int *D_680 = iftmp_1 + iftmp_2;
> 	  D_681 = *D_680;
> 	  __asm volatile ("lfence");
> 	}      
>     }
>   else
>     {
>       int *D_680 = iftmp_1 + iftmp_2;
>       D_681 = *D_680;
>     }
>  
> which generates code like:
> 
>         movl    _otable_p+4, %eax
>         cmpl    $0, %eax
>         jle     .L9
>         movl    (%eax,%eax,4), %ebx
> .L6:
> 	...
> 
> .L9:
>         je      .L10
>         negl    %eax
>         movl    (%eax,%eax,4), %ebx
>         lfence
> 	...
> 
> .L10:
>         movl    $1, (%esp)
>         call    _Jv_ThrowNoSuchFieldError
> 
> In a way this isn't bad, because the commonly-executed path is no
> longer than it is at the present time.  However, the additional code
> bloat is quite substantial.
> 
> Compiler-generated get and put methods would present a cleaner way to do
> this: it'd be
> 
>     D.701 = D.700->vtable;
>     D.702 = _otable_p[1];
>     D.704 = D.701 + D.702;
>     D.707 = (*D.704) (D.699);
> 
> which generates code like
>          
>         movl    (%eax), %edx    # <variable>.vtable, <variable>.vtable
>         movl    %eax, (%esp)    # D.700,
>         movl    _otable_p+4, %eax       # _otable_p, _otable_p
>         call    *(%edx,%eax)    #* _otable_p
> 
> which removes the need to test for ThrowNoSuchFieldError, volatile,
> etc.
> 
> A fence instruction would be inserted into the accessor methods iff
> the field was known to be volatile: the compiler has the advantage
> that this is known at compile time at the point of declaration.
> However, there would be the additional code bloat of the accessor
> methods and their vtable entries.  Also, this changes the ABI in an
> incompatible way.
> 
> There is one other idea that I am somewhat reluctant to contemplate:
> always emit a fence when accessing volatiles declared outside the
> current class.  This might not be so awful, because accessing fields
> outside the current class is not much done in well-written
> object-oriented code.  However, these fences can be quite slow on
> some architectures.
> 
> Your opinion is welcomed...

How about a hybrid approach:

otable_entry e = _otable_p[i];
if (can_do_direct_access_p(e))
	target = *(other_object + e);
else
	target = (*e)();


At compile time you generate an accessor method if the volatile is 
accessible from outside of the class.  Since most fields are not 
volatile, this should not be too much overhead.

At link time, fill in the otable entry with either the offset to the 
field (if not volatile), or a pointer to the accessor function.  You 
also put a pointer to _Jv_ThrowNoSuchFieldError in the otable slots 
where it would be appropriate.

The trick is to have a very efficient can_do_direct_access_p().  A naive 
implementation might be:

boolean inline can_do_direct_access_p(void *x) { return x & ~0xffff;}

It might be possible to implement this as efficiently as the current 
otable accesses.

David Daney.



More information about the Java mailing list