Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackMachine#eval is not compiling to a switch #1

Open
armanbilge opened this issue Dec 20, 2023 · 4 comments
Open

StackMachine#eval is not compiling to a switch #1

armanbilge opened this issue Dec 20, 2023 · 4 comments

Comments

@armanbilge
Copy link

Currently the loop (de-)compiles to this. Notice the series of if statements and the dispatch on code().

private final double loop$2(int sp, int dp, int pc) {
    byte by;
    while (true) {
        if (pc == IArray.package.IArray$.MODULE$.size((Object)this.program())) {
            return this.stack[sp - 1];
        }
        by = IArray.package.IArray$.MODULE$.apply(this.program(), pc);
        if (ByteCode.Op$.Lit.code() == by) {
            this.stack[sp] = IArray.package.IArray$.MODULE$.apply(this.data(), dp);
            int n = sp + 1;
            int n2 = dp + 1;
            int n3 = pc + 1;
            sp = n;
            dp = n2;
            pc = n3;
            continue;
        }
        if (ByteCode.Op$.Add.code() == by) {
            double a = this.stack[sp - 1];
            double b = this.stack[sp - 2];
            this.stack[sp - 2] = a + b;
            int n = sp - 1;
            int n4 = pc + 1;
            sp = n;
            pc = n4;
            continue;
        }
        if (ByteCode.Op$.Sub.code() == by) {
            double a = this.stack[sp - 1];
            double b = this.stack[sp - 2];
            this.stack[sp - 2] = a - b;
            int n = sp - 1;
            int n5 = pc + 1;
            sp = n;
            pc = n5;
            continue;
        }
        if (ByteCode.Op$.Mul.code() == by) {
            double a = this.stack[sp - 1];
            double b = this.stack[sp - 2];
            this.stack[sp - 2] = a * b;
            int n = sp - 1;
            int n6 = pc + 1;
            sp = n;
            pc = n6;
            continue;
        }
        if (ByteCode.Op$.Div.code() != by) break;
        double a = this.stack[sp - 1];
        double b = this.stack[sp - 2];
        this.stack[sp - 2] = a / b;
        int n = sp - 1;
        int n7 = pc + 1;
        sp = n;
        pc = n7;
    }
    throw new MatchError((Object)BoxesRunTime.boxToByte((byte)by));
}

If I apply this patch ...

--- a/core/src/main/scala/arithmetic/ByteCode.scala
+++ b/core/src/main/scala/arithmetic/ByteCode.scala
@@ -117,25 +117,25 @@ object ByteCode {
         if (pc == program.size) stack(sp - 1)
         else
           program(pc) match {
-            case Op.Lit.code =>
+            case 0 =>
               stack(sp) = data(dp)
               loop(sp + 1, dp + 1, pc + 1)
-            case Op.Add.code =>
+            case 1 =>
               val a = stack(sp - 1)
               val b = stack(sp - 2)
               stack(sp - 2) = (a + b)
               loop(sp - 1, dp, pc + 1)
-            case Op.Sub.code =>
+            case 2 =>
               val a = stack(sp - 1)
               val b = stack(sp - 2)
               stack(sp - 2) = (a - b)
               loop(sp - 1, dp, pc + 1)
-            case Op.Mul.code =>
+            case 3 =>
               val a = stack(sp - 1)
               val b = stack(sp - 2)
               stack(sp - 2) = (a * b)
               loop(sp - 1, dp, pc + 1)
-            case Op.Div.code =>
+            case 4 =>
               val a = stack(sp - 1)
               val b = stack(sp - 2)
               stack(sp - 2) = (a / b)

Then it decompiles to this switch statement.

    private final double loop$2(int sp, int dp, int pc) {
        byte by;
        block7: while (true) {
            if (pc == IArray.package.IArray$.MODULE$.size((Object)this.program())) {
                return this.stack[sp - 1];
            }
            by = IArray.package.IArray$.MODULE$.apply(this.program(), pc);
            switch (by) {
                case 0: {
                    this.stack[sp] = IArray.package.IArray$.MODULE$.apply(this.data(), dp);
                    int n = sp + 1;
                    int n2 = dp + 1;
                    int n3 = pc + 1;
                    sp = n;
                    dp = n2;
                    pc = n3;
                    continue block7;
                }
                case 1: {
                    double a = this.stack[sp - 1];
                    double b = this.stack[sp - 2];
                    this.stack[sp - 2] = a + b;
                    int n = sp - 1;
                    int n4 = pc + 1;
                    sp = n;
                    pc = n4;
                    continue block7;
                }
                case 2: {
                    double a = this.stack[sp - 1];
                    double b = this.stack[sp - 2];
                    this.stack[sp - 2] = a - b;
                    int n = sp - 1;
                    int n5 = pc + 1;
                    sp = n;
                    pc = n5;
                    continue block7;
                }
                case 3: {
                    double a = this.stack[sp - 1];
                    double b = this.stack[sp - 2];
                    this.stack[sp - 2] = a * b;
                    int n = sp - 1;
                    int n6 = pc + 1;
                    sp = n;
                    pc = n6;
                    continue block7;
                }
                case 4: {
                    double a = this.stack[sp - 1];
                    double b = this.stack[sp - 2];
                    this.stack[sp - 2] = a / b;
                    int n = sp - 1;
                    int n7 = pc + 1;
                    sp = n;
                    pc = n7;
                    continue block7;
                }
            }
            break;
        }
        throw new MatchError((Object)BoxesRunTime.boxToByte((byte)by));
    }

I wonder if this explains some of your performance results.

@noelwelsh
Copy link
Contributor

For reference, and because I was wondering, the code was decompiled with CFR

@arosien
Copy link

arosien commented Dec 23, 2023

For reference, and because I was wondering, the code was decompiled with CFR

@armanbilge said, via discord:

right-click the classfiles in VSCode+Metals, there's an option "decompile with CFR"

@arosien
Copy link

arosien commented Dec 23, 2023

I recalled the @switch annotation from a discussion in Cats Effect, and the compiler does report it isn't able to generate one.

- program(pc) match {
+ (program(pc): @switch) match {

outputs

Could not emit switch for @switch annotated match

@arosien
Copy link

arosien commented Jan 12, 2024

I tested a version with @switch and it was slower. Yay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants