From 1a5b222c72f56e089c3a82c8e06da90667123e1a Mon Sep 17 00:00:00 2001 From: ice1000 Date: Sat, 4 Jan 2025 12:43:13 -0500 Subject: [PATCH 1/2] optimize: initial optimizer, add `Unreachable` syntax node Co-authored-by: Hoshino Tented --- .../aya/compiler/free/FreeCodeBuilder.java | 6 ++- .../morphism/free/FreeCodeBuilderImpl.java | 16 +++---- .../free/morphism/free/FreeOptimizer.java | 47 +++++++++++++++++++ .../free/morphism/free/FreeRunner.java | 16 ++----- .../compiler/free/morphism/free/FreeStmt.java | 3 +- .../compiler/serializers/AyaSerializer.java | 12 +---- .../serializers/JitTeleSerializer.java | 4 +- .../serializers/ModuleSerializer.java | 2 + .../serializers/PatternSerializer.java | 4 +- 9 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java b/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java index 7de35bde0d..b899ae416c 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java +++ b/jit-compiler/src/main/java/org/aya/compiler/free/FreeCodeBuilder.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.compiler.free; @@ -60,4 +60,8 @@ void switchCase( ); void returnWith(@NotNull FreeJavaExpr expr); + + default void unreachable() { + returnWith(invoke(Constants.PANIC, ImmutableSeq.empty())); + } } diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeCodeBuilderImpl.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeCodeBuilderImpl.java index dcc3ae8fe9..02572c4b19 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeCodeBuilderImpl.java +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeCodeBuilderImpl.java @@ -153,6 +153,10 @@ public void switchCase( stmts.append(new FreeStmt.Return(assertFreeExpr(expr))); } + @Override public void unreachable() { + stmts.append(FreeStmt.Unreachable.INSTANCE); + } + @Override public @NotNull FreeJavaExpr mkNew(@NotNull MethodRef conRef, @NotNull ImmutableSeq args) { return FreeExprBuilderImpl.INSTANCE.mkNew(conRef, args); @@ -191,16 +195,8 @@ public void switchCase( return FreeExprBuilderImpl.INSTANCE.mkLambda(captures, method, builder); } - @Override - public @NotNull FreeJavaExpr iconst(int i) { - return FreeExprBuilderImpl.INSTANCE.iconst(i); - } - - @Override - public @NotNull FreeJavaExpr iconst(boolean b) { - return FreeExprBuilderImpl.INSTANCE.iconst(b); - } - + @Override public @NotNull FreeJavaExpr iconst(int i) { return FreeExprBuilderImpl.INSTANCE.iconst(i); } + @Override public @NotNull FreeJavaExpr iconst(boolean b) { return FreeExprBuilderImpl.INSTANCE.iconst(b); } @Override public @NotNull FreeJavaExpr aconst(@NotNull String value) { return FreeExprBuilderImpl.INSTANCE.aconst(value); } diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java new file mode 100644 index 0000000000..0a5613519d --- /dev/null +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java @@ -0,0 +1,47 @@ +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. +// Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. +package org.aya.compiler.free.morphism.free; + +import kala.collection.immutable.ImmutableSeq; +import org.aya.compiler.free.morphism.free.FreeDecl.Clazz; +import org.aya.compiler.free.morphism.free.FreeDecl.ConstantField; +import org.aya.compiler.free.morphism.free.FreeDecl.Method; +import org.jetbrains.annotations.NotNull; + +public interface FreeOptimizer { + static @NotNull Clazz optimizeClass(Clazz clazz) { + return (Clazz) optimize(clazz); + } + + static @NotNull FreeDecl optimize(FreeDecl decl) { + return switch (decl) { + case Clazz(var metadata, var owner, var nested, var superclass, var members) -> { + var newMembers = members.map(FreeOptimizer::optimize); + yield new Clazz(metadata, owner, nested, superclass, newMembers); + } + case ConstantField field -> field; + case Method(var signature, var body) -> new Method(signature, body.flatMap(FreeOptimizer::optimize)); + }; + } + + static @NotNull ImmutableSeq optimize(FreeStmt stmt) { + return switch (stmt) { + case FreeStmt.Switch(var elim, var cases, var branch, var defaultCase) -> { + branch = branch.map(it -> it.flatMap(FreeOptimizer::optimize)); + defaultCase = defaultCase.flatMap(FreeOptimizer::optimize); + var defaultPanic = defaultCase.sizeEquals(1) && defaultCase.getFirst() == FreeStmt.Unreachable.INSTANCE; + if (defaultPanic && branch.sizeEquals(1)) { + yield branch.getFirst(); + } + yield ImmutableSeq.of(new FreeStmt.Switch(elim, cases, branch, defaultCase)); + } + case FreeStmt.Breakable(var stmts) -> ImmutableSeq.of(new FreeStmt.Breakable(stmts.flatMap(FreeOptimizer::optimize))); + case FreeStmt.IfThenElse(var cond, var thenBlock, var elseBlock) -> { + var newThenBlock = thenBlock.flatMap(FreeOptimizer::optimize); + var newElseBlock = elseBlock == null ? null : elseBlock.flatMap(FreeOptimizer::optimize); + yield ImmutableSeq.of(new FreeStmt.IfThenElse(cond, newThenBlock, newElseBlock)); + } + default -> ImmutableSeq.of(stmt); + }; + } +} diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeRunner.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeRunner.java index f2fd29988a..2e710610d2 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeRunner.java +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeRunner.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.compiler.free.morphism.free; @@ -121,6 +121,7 @@ private void runFree(@NotNull ArgumentProvider ap, @NotNull FreeCodeBuilder buil private void runFree(@NotNull ArgumentProvider ap, @NotNull FreeCodeBuilder builder, @NotNull FreeStmt free) { switch (free) { case FreeStmt.Break _ -> builder.breakOut(); + case FreeStmt.Unreachable _ -> builder.unreachable(); case FreeStmt.Breakable(var inner) -> builder.breakable(cb -> runFree(ap, cb, inner)); case FreeStmt.DeclareVariable mkVar -> bindVar(mkVar.theVar().index(), builder.makeVar(mkVar.type(), null)); case FreeStmt.Exec exec -> builder.exec(runFree(ap, builder, exec.expr())); @@ -185,17 +186,8 @@ private void bindVar(int index, @NotNull LocalVariable userVar) { private class SubscopeHandle implements AutoCloseable { private final @UnknownNullability MutableMap oldBinding = binding; - - public SubscopeHandle( - @NotNull MutableMap newScope - ) { - binding = newScope; - } - - @Override - public void close() { - binding = oldBinding; - } + public SubscopeHandle(@NotNull MutableMap newScope) { binding = newScope; } + @Override public void close() { binding = oldBinding; } } private @NotNull SubscopeHandle subscoped() { diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeStmt.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeStmt.java index e38283af91..32082af3b7 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeStmt.java +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeStmt.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.compiler.free.morphism.free; @@ -32,6 +32,7 @@ record IfThenElse(@NotNull Condition cond, @NotNull ImmutableSeq thenB record Breakable(@NotNull ImmutableSeq block) implements FreeStmt { } enum Break implements FreeStmt { INSTANCE } + enum Unreachable implements FreeStmt { INSTANCE } record Exec(@NotNull FreeExpr expr) implements FreeStmt { } record Switch(@NotNull FreeVariable elim, @NotNull ImmutableIntSeq cases, diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java index d1b9500385..ca7f4c617b 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/AyaSerializer.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.compiler.serializers; @@ -22,16 +22,8 @@ public interface AyaSerializer { String FIELD_EMPTYCALL = "ourCall"; String CLASS_PANIC = getJavaRef(Panic.class); - static void returnPanic(@NotNull FreeCodeBuilder builder) { - builder.returnWith(buildPanic(builder)); - } - static void execPanic(@NotNull FreeCodeBuilder builder) { - builder.exec(buildPanic(builder)); - } - - static @NotNull FreeJavaExpr buildPanic(@NotNull FreeExprBuilder builder) { - return builder.invoke(Constants.PANIC, ImmutableSeq.empty()); + builder.exec(builder.invoke(Constants.PANIC, ImmutableSeq.empty())); } /** diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java index db6fc18a81..04e252043c 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/JitTeleSerializer.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. package org.aya.compiler.serializers; @@ -105,7 +105,7 @@ protected void buildTelescope(@NotNull FreeCodeBuilder builder, @NotNull T unit, cb.returnWith(result); }, - AyaSerializer::returnPanic); + builder1 -> builder1.unreachable()); } /** diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java index 1ff00b378f..5d6f323a51 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/ModuleSerializer.java @@ -7,6 +7,7 @@ import org.aya.compiler.free.FreeClassBuilder; import org.aya.compiler.free.FreeJavaBuilder; import org.aya.compiler.free.morphism.free.FreeJavaBuilderImpl; +import org.aya.compiler.free.morphism.free.FreeOptimizer; import org.aya.compiler.free.morphism.free.FreeRunner; import org.aya.compiler.free.morphism.source.SourceFreeJavaBuilder; import org.aya.compiler.serializers.MatchySerializer.MatchyData; @@ -80,6 +81,7 @@ private void doSerialize(@NotNull FreeClassBuilder builder, @NotNull TyckDef uni public String serializeWithBestBuilder(ModuleResult unit) { var freeJava = serialize(FreeJavaBuilderImpl.INSTANCE, unit); + freeJava = FreeOptimizer.optimizeClass(freeJava); return new FreeRunner<>(SourceFreeJavaBuilder.create()).runFree(freeJava); } diff --git a/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java index e84b12b215..09be664012 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/serializers/PatternSerializer.java @@ -84,7 +84,7 @@ private void doSerialize( switch (pat) { case Pat.Misc misc -> { switch (misc) { - case Absurd -> AyaSerializer.execPanic(builder); + case Absurd -> builder.unreachable(); case UntypedBind -> { onMatchBind(builder, term); onMatchSucc.accept(builder); @@ -264,7 +264,7 @@ public PatternSerializer serialize(@NotNull FreeCodeBuilder builder, @NotNull Im assert i > 0; var realIdx = i - 1; unit.get(realIdx).onSucc.accept(this, mBuilder, bindSize.get(realIdx)); - }, AyaSerializer::returnPanic); + }, builder1 -> builder1.unreachable()); return this; } From 3999edeb3dc207422ea089c310201e9cfda2437b Mon Sep 17 00:00:00 2001 From: ice1000 Date: Sat, 4 Jan 2025 12:52:55 -0500 Subject: [PATCH 2/2] test: make rbtree test use the optimizer --- .../compiler/free/morphism/free/FreeOptimizer.java | 13 +++++++++---- jit-compiler/src/test/java/CompileTest.java | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java index 0a5613519d..68c0954283 100644 --- a/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java +++ b/jit-compiler/src/main/java/org/aya/compiler/free/morphism/free/FreeOptimizer.java @@ -29,13 +29,18 @@ case Clazz(var metadata, var owner, var nested, var superclass, var members) -> case FreeStmt.Switch(var elim, var cases, var branch, var defaultCase) -> { branch = branch.map(it -> it.flatMap(FreeOptimizer::optimize)); defaultCase = defaultCase.flatMap(FreeOptimizer::optimize); - var defaultPanic = defaultCase.sizeEquals(1) && defaultCase.getFirst() == FreeStmt.Unreachable.INSTANCE; - if (defaultPanic && branch.sizeEquals(1)) { - yield branch.getFirst(); + if (branch.isEmpty()) yield defaultCase; + if (defaultCase.sizeEquals(1) && defaultCase.getFirst() == FreeStmt.Unreachable.INSTANCE) { + if (branch.sizeEquals(1)) { + yield branch.getFirst(); + } else if (branch.sizeGreaterThan(1)) { + yield ImmutableSeq.of(new FreeStmt.Switch(elim, cases.dropLast(1), branch.dropLast(1), branch.getLast())); + } } yield ImmutableSeq.of(new FreeStmt.Switch(elim, cases, branch, defaultCase)); } - case FreeStmt.Breakable(var stmts) -> ImmutableSeq.of(new FreeStmt.Breakable(stmts.flatMap(FreeOptimizer::optimize))); + case FreeStmt.Breakable(var stmts) -> + ImmutableSeq.of(new FreeStmt.Breakable(stmts.flatMap(FreeOptimizer::optimize))); case FreeStmt.IfThenElse(var cond, var thenBlock, var elseBlock) -> { var newThenBlock = thenBlock.flatMap(FreeOptimizer::optimize); var newElseBlock = elseBlock == null ? null : elseBlock.flatMap(FreeOptimizer::optimize); diff --git a/jit-compiler/src/test/java/CompileTest.java b/jit-compiler/src/test/java/CompileTest.java index 44eaceb566..38035d367c 100644 --- a/jit-compiler/src/test/java/CompileTest.java +++ b/jit-compiler/src/test/java/CompileTest.java @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tesla (Yinsen) Zhang. +// Copyright (c) 2020-2025 Tesla (Yinsen) Zhang. // Use of this source code is governed by the MIT license that can be found in the LICENSE.md file. import kala.collection.immutable.ImmutableSeq; @@ -104,7 +104,7 @@ public record TyckResult(@NotNull ImmutableSeq defs, @NotNull ResolveIn public static @NotNull String serializeFrom(@NotNull TyckResult result) { return new ModuleSerializer(result.info.shapeFactory()) - .serialize(SourceFreeJavaBuilder.create(), new ModuleSerializer.ModuleResult( + .serializeWithBestBuilder(new ModuleSerializer.ModuleResult( DumbModuleLoader.DUMB_MODULE_NAME, result.defs.filterIsInstance(TopLevelDef.class))); }