Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

vmkit: lazy compilation fails when triggered from AOT compiled code #8226

Open Quuxplusone opened 14 years ago

Quuxplusone commented 14 years ago
Bugzilla Link PR7822
Status NEW
Importance P normal
Reported by Allan Tong (actong88@gmail.com)
Reported on 2010-08-04 19:54:08 -0700
Last modified on 2010-08-21 10:57:01 -0700
Version unspecified
Hardware PC Linux
CC llvm-bugs@lists.llvm.org, nicolas.geoffray@gmail.com
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
Using the default J3 lazy compiler, AOT compiled code that calls a stub and
triggers a lazy compilation will crash vmkit.  The problem appears to be that
the J3 lazy compiler uses debug information of the caller of the stub to
determine the constant pool entry that corresponds with the stubbed method.
AOT compiled code is missing this debug information, which causes vmkit to
crash.

Test case I've been using is trying to run the dacapo benchmarks
(www.dacapobench.org).  Run the following with a precompiled libvmjc.so:

$ CLASSPATH=dacapo-9.12-bach.jar vmkit -java Harness -l
I received a SIGSEGV: either the VM code or an external
native method is bogus. Aborting...
Segmentation fault
$

Using the LLVM lazy compiler works (as does running without libvmjc.so):

$ CLASSPATH=dacapo-9.12-bach.jar vmkit -llvm-lazy -java Harness -l
avrora batik eclipse fop h2 jython luindex lusearch pmd sunflow tomcat
tradebeans tradesoap xalan
$
Quuxplusone commented 14 years ago

Thanks Allan for the report.

So what had you AOT-compiled? libvmjc.so and some classes from Dacapo I guess? Did you try AOT-compiling all the dependencies of dacapo?

Note that the --llvm-lazy only works for a non-precise GC, and won't work in a multi-threaded environment, so we should definitely implement this functionality with the J3 lazy compiler. This means we either need to emit the byte array of the AOT-compiled class or locate it at runtime.

Also, I believe most of the benchmarks in the new version of dacapo don't work with GNU Classpath...

Quuxplusone commented 14 years ago

I only AOT compiled libvmjc.so. I didn't try AOT compiling anything else.

Looking back at my original report, it looks like it wasn't as detailed as it could have been, so just to clarify, the "debug" information I was referring to is the CodeLineInfo corresponding to the callsite of the stub, which j3ResolveVirtualStub uses to determine the constant pool entry in the caller's class that specifies which method to compile. As can be seen in JavaAOTCompiler::CreateConstantFromJavaMethod, CodeLineInfo is not generated for AOT compiled code. I suppose this could be because it wasn't expected that AOT code would be mixed with JIT code. Is this an unsupported use case?

As far as solutions go, can the method information not be encoded within the stubs themselves? The stub would then pass that information to the resolve function (e.g. j3ResolveVirtualStub). This would avoid the need for the resolver to walk up the stack and find the constant pool entry for the method to be compiled. It should be much easier to determine the method being stubbed when generating the stubs instead of trying to do it in the resolve function. Also, with this change I believe that there would be no need for generating CodeLineInfo at all.

Quuxplusone commented 14 years ago
(In reply to comment #2)
> I only AOT compiled libvmjc.so.  I didn't try AOT compiling anything else.

Hmm, libvmjc.so should be self-sufficient. Can you get a backtrace and see who
calls the stub? There should be no stub at all in libvmjc.so.

> I suppose this could be because it wasn't expected that
> AOT code would be mixed with JIT code.  Is this an unsupported use case?

No, AOT and JIT code can perfectly be mixed.

>
> As far as solutions go, can the method information not be encoded within the
> stubs themselves?  The stub would then pass that information to the resolve
> function (e.g. j3ResolveVirtualStub).  This would avoid the need for the
> resolver to walk up the stack and find the constant pool entry for the method
> to be compiled.  It should be much easier to determine the method being
stubbed
> when generating the stubs instead of trying to do it in the resolve function.
> Also, with this change I believe that there would be no need for generating
> CodeLineInfo at all.

How would you pass that information? The caller expects the call entry to be
the right function and not the stub, so it only passes the arguments to the
call.
Quuxplusone commented 14 years ago
(In reply to comment #3)
> Hmm, libvmjc.so should be self-sufficient. Can you get a backtrace and see who
> calls the stub? There should be no stub at all in libvmjc.so.

Unfortunately I don't have a compiled libvmjc.so at the moment, and it'll take
me a while to build another one.  However, I can describe the scenario.

The stub is not in libvmjc.so but in JIT-compiled code.  In my case, this
happened to be the equals(Object) method of some class in dacapo.  Instances of
this class are added to a HashMap, which is in libvmjc.so.  In the process of
adding entries into the map, the map calls the equals(Object) method of the
key, which is at this point still a stub.  The stub calls j3ResolveVirtualStub,
which walks up the stack to find the caller, i.e. AbstractMap.equals(Object,
Object) in Classpath.  However, since AbstractMap was AOT-compiled, it doesn't
include CodeLineInfo, which results in a null dereference in
j3ResolveVirtualStub.

> > As far as solutions go, can the method information not be encoded within the
> > stubs themselves?  The stub would then pass that information to the resolve
> > function (e.g. j3ResolveVirtualStub).  This would avoid the need for the
> > resolver to walk up the stack and find the constant pool entry for the
method
> > to be compiled.  It should be much easier to determine the method being
stubbed
> > when generating the stubs instead of trying to do it in the resolve
function.
> > Also, with this change I believe that there would be no need for generating
> > CodeLineInfo at all.
>
> How would you pass that information? The caller expects the call entry to be
> the right function and not the stub, so it only passes the arguments to the
> call.

Perhaps I'm missing something (could very well be, I'm pretty new to all this).
This is the stub I see generated for Object.equals():

define internal i8 @1(%JavaObject*, %JavaObject*) {
enter:
  %2 = call i8* @j3ResolveVirtualStub(%JavaObject* %0) ; <i8*> [#uses=2]
  %3 = icmp eq i8* null, %2                       ; <i1> [#uses=1]
  br i1 %3, label %end, label %call

end:                                              ; preds = %call, %enter
  %4 = phi i8 [ 0, %enter ], [ %6, %call ]        ; <i8> [#uses=1]
  ret i8 %4

call:                                             ; preds = %enter
  %5 = bitcast i8* %2 to i8 (%JavaObject*, %JavaObject*)* ; <i8 (%JavaObject*, %JavaObject*)*> [#uses=1]
  %6 = call i8 %5(%JavaObject* %0, %JavaObject* %1) ; <i8> [#uses=1]
  br label %end
}

Can the call to j3ResolveVirtualStub not be changed to something like

  %2 = call i8* @j3ResolveVirtualStub(%JavaObject* %0, ...methodID...)

where methodID is a set of arguments that identify the method to be compiled?
I'm not sure exactly what would need to go into methodID, but the main point is
that the information identifying the method to be compiled would be hardcoded
into the stub.  This should all be transparent to the caller of the stub.

What I'm not clear about is whether a stub is created per method or per method
signature.  If multiple methods with the same signature share the same stub
then I guess this wouldn't work.
Quuxplusone commented 14 years ago
> The stub is not in libvmjc.so but in JIT-compiled code.  In my case, this
> happened to be the equals(Object) method of some class in dacapo.  Instances
of
> this class are added to a HashMap, which is in libvmjc.so.  In the process of
> adding entries into the map, the map calls the equals(Object) method of the
> key, which is at this point still a stub.  The stub calls
j3ResolveVirtualStub,
> which walks up the stack to find the caller, i.e. AbstractMap.equals(Object,
> Object) in Classpath.  However, since AbstractMap was AOT-compiled, it doesn't
> include CodeLineInfo, which results in a null dereference in
> j3ResolveVirtualStub.

Really nice debugging, OK, I get the issue now.

>
> > > As far as solutions go, can the method information not be encoded within
the
> > > stubs themselves?

They could, except that a stub is as of today per-signature and not per-method.
A stub per-method may be too memory expensive, so I'd rather find a way to emit
the CodeLineInfo, but they depend on LLVM. I haven't followed the "address of a
label" feature too much, but maybe there is a way to use them and encode the
call location in the CodeLineInfo.