Search code examples
bytecodejava-bytecode-asmjvm-bytecode

Implementing comparison operators in Bytecode using ASM


I'm working on a personal project of mine creating a simple language which is compiled to Java Bytecode. I'm using the ASM library version 7.3.1 but I've hit a problem with Frames that I can't quite figure out.

This is actually two questions rolled into one. I'm trying to implement simple comparison operators e.g. >, <, >= etc. These operators should return a boolean result obviously. I can't see a way of implementing this directly in Bytecode so I'm using using FCMPG to compare two floats that are already on the stack and then using IFxx to push either a 1 or 0 to the stack depending on which operator I'm generating code for.

For example here is my code for >:

val label = new Label()
mv.visitInsn(FCMPG)           // mv is my MethodVisitor, there are 2 Floats on the stack
mv.visitJumpInsn(IFGT, label)
mv.visitInsn(ICONST_1)
mv.visitLabel(label)
mv.visitInsn(ICONST_0)

Question 1: Is this the correct approach for implementing comparison operators or am I missing a simpler method?

Question 2: Running this code generates this error:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at org.objectweb.asm.Frame.merge(Frame.java:1268)
    at org.objectweb.asm.Frame.merge(Frame.java:1244)
    at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1610)
    at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1546)
    at compiler.codegen.default$$anon$1.generateConstructor(default.scala:138)
    at compiler.codegen.default$$anon$1.generateCode(default.scala:157)
    at compiler.codegen.default$$anon$1.generateCode(default.scala:21)
    at compiler.codegen.package$.generateCode(package.scala:21)
    at compiler.codegen.package$CodeGeneratorOp.generateCode(package.scala:17)
    at Main$.main(main.scala:27)
    at Main.main(main.scala)

I know this is to do with Frames but I don't really understand frames enough to know what I'm doing wrong. I've tried adding mv.visitFrame(F_SAME, 0, null, 0, null) after visitLabel but I get the same error.


Solution

  • 1) Yes, this is the correct way to do it. I believe the actual Java compiler does something very similar.

    2) You get a verification error because you forgot to add a jump to the end of the if block. If you look closely at your code, you'll see that when the jump isn't taken both branches are executed and you end up with both 0 and 1 on the stack, which leads to a verification error. You need to insect a second jump so only the constant you wish gets pushed to the stack in this case. It should be something like this:

    val then_label = new Label()
    val end_label = new Label()
    mv.visitInsn(FCMPG)           // mv is my MethodVisitor, there are 2 Floats on the stack
    mv.visitJumpInsn(IFGT, then_label)
    mv.visitInsn(ICONST_1)
    mv.visitGoto(end_label)
    mv.visitLabel(then_label)
    mv.visitInsn(ICONST_0)
    mv.visitLabel(end_label)