Team-CC-Corp / JVML-JIT

A continuation of JVML which JITs Java bytecode to Lua bytecode rather than interpreting.
MIT License
30 stars 4 forks source link

Crash when starting the program #72

Closed JBYoshi closed 9 years ago

JBYoshi commented 9 years ago

java.lang.Object.<clinit>()V/bytecode:15: attempt to index ? (a nil value) classloader.lua:745: Error in java.lang.Object <clinit>()V

After some testing, it seems that Object.registerNatives() needs to be implemented.

ElvishJerricco commented 9 years ago

No, 'Object.registerNatives()' is implemented in a weird spot because it's the only native that needs to be implemented manually by the VM.

Can you show me the code that you're trying to run?

JBYoshi commented 9 years ago
import cc.turtle.Turtle;
public final class Robot {
  private final Turtle t = Turtle.getInstance();
  public boolean goForward() {
    return t.forward();
  }
  public boolean goBack() {
    return t.back();
  }
  public void turnLeft() {
    t.turnLeft();
  }
  public void turnRight() {
    t.turnRight();
  }
  public boolean isFinished() {
    return !t.detectDown();
  }
  public static void main(String[] args) {
    Robot robot = new Robot();
    while (!robot.isFinished()) {
      robot.turnLeft();
      if (robot.goForward()) continue;
      robot.turnRight();
      if (robot.goForward()) continue;
      robot.turnRight();
      if (robot.goForward()) continue;
      robot.turnRight();
      if (robot.goForward()) continue;
      robot.turnRight();
      robot.turnRight();
    }
  }
}
ElvishJerricco commented 9 years ago

Hm it seems to be an issue with the 1.74 update. This could take some digging...

ElvishJerricco commented 9 years ago

@Yevano I have no idea why it worked before, but it seems we have an issue with invokestatic on empty stacks.

java.lang.Object.<clinit>()V
Length: 24
Locals: 0
    [1] GETTABLE 1 0 256     ;Pushing stacktrace
                             ;Loading and calling function
    [2] GETTABLE 2 0 257
    [3] GETTABLE 3 0 258
    [4] GETTABLE 4 0 259
    [5] GETTABLE 5 0 260
    [6] CALL 1 5 1

[0] B8:
    [7] GETTABLE 4 0 261     ;Setting line number
                             ;Loading and calling function
    [8] GETTABLE 5 0 262
    [9] CALL 4 2 1
    [10] MOVE 3 0
    [11] GETTABLE 0 0 263
    [12] CALL 0 1 3          ;java.lang.Object.registerNatives()V
    [13] TEST 1 0            ;Check throw
    [14] JMP 5
    [15] GETTABLE 4 0 264    ;Throw
                             ;Popping stacktrace
                             ;Loading and calling function
    [16] CALL 4 1 1
    [17] LOADNIL 4 4
    [18] MOVE 5 1
    [19] RETURN 4 3
    [20] MOVE 2 1
    [21] MOVE 1 0
    [22] MOVE 0 3

This is the assembly being generated. You can see on line 15 which GETTABLE instruction is erroring. It's because we're doing that trick, where we move the object beneath all the method arguments to a register above those arguments, so that we don't have to move each argument up a register to inject the function beneath them. With an empty stack, this end up trying to move the RTI table above the stack, which leads to major problems when we try to access the RTI table before moving back to place, like on line 15.

ElvishJerricco commented 9 years ago

Actually, does this problem exist in all the invokes? If the invocation target is included as part of the arguments list (which it is), and there's nothing beneath that target besides RTI, shouldn't we get the same problem?

ElvishJerricco commented 9 years ago

I should also mention, I think the reason we never saw the issue before now is the fact that it will only appear when an exception is thrown. You'll notice that the invoke instructions don't use RTI after stowing it above the stack until it moves it back. Except when an exception is thrown, and it needs to use RTI to pop the stack trace. I imagine we just haven't had an instance where an exception was thrown in an invocation on an empty stack.

I suppose one solution could be to move the rsave object (in this case, RTI) back in place before the throw code, but that seems hacky to me compared to just not stowing a value that's technically beneath the stack.

ElvishJerricco commented 9 years ago

One final detail I just realized: The issue should also only occur when a static method invokes a method. Being within a static method is the only way to not have a local variable to stow sitting above the RTI.

Yevano commented 9 years ago

@ElvishJerricco In that case, the simplest solution would be to simply skip a cell in the stack when we enter a static method, right? If possible, I don't want to go back to the original way of doing it where we move all the registers up, because that's slower.

ElvishJerricco commented 9 years ago

What do you mean by that?

Also, I think we should generally avoid moving items that aren't on the stack, such as local variables and RTI. Because if we do anything unexpected, like throwing an exception and moving the frame to a catch block, we want those areas to be untouched.

The other alternative is we can move rsave back to rx before the TEST instruction in asmCheckThrow. But since that solution is specific to certain scenarios, we can't just implement it in asmCheckThrow, which would end up making this solution pretty ugly.

ElvishJerricco commented 9 years ago

Working on fixing this. I'm generalizing the invoke instructions. And I'm making it so the stack is moved up in the case that there is nothing beneath the arguments on the stack. This means no moving local variables, because of the danger of misplaced locals in finally blocks.

ElvishJerricco commented 9 years ago

I'm encountering a very strange problem. It seems arguments aren't being passed into JIT'd functions at all. Considering Dan didn't change the LuaJ version, there is no apparent reason for this problem.

ElvishJerricco commented 9 years ago

Testing under 1.7, everything works. But under 1.74, arguments aren't passed to JIT'd functions. Wut.

ElvishJerricco commented 9 years ago

Hm upon further inspection, it seems the values are properly passed in, but for some reason they're failing null pointer checks anyway.

ElvishJerricco commented 9 years ago

Ok I finally found the issue. The bit API is misbehaving. I tried switching to the new bit32, but it exhibits the same behavior.

oldmud0 commented 9 years ago

Huh? Looks like development is no longer dormant.

ElvishJerricco commented 9 years ago

I always watch the repo for breaking bugs like this, and for pull requests. So it's always open. We'll see if I start working on the runtime some more though.

JBYoshi commented 9 years ago

It works now. Thanks!