diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 61ec116ec..5e198afc3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -937,38 +937,34 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit module.functions.add(function); var instanceLocal = new WasmLocal(standardClasses.objectClass().getType(), "instance"); var originalLocal = new WasmLocal(objectStructure.getReference(), "original"); - var resultLocal = new WasmLocal(objectStructure.getReference(), "result"); - var originalDataLocal = new WasmLocal(arrayType.getReference(), "originalData"); - var dataCopyLocal = new WasmLocal(arrayType.getReference(), "resultData"); + var originalDataLocal = new WasmLocal(arrayType.getNonNullReference(), "originalData"); + var dataCopyLocal = new WasmLocal(arrayType.getNonNullReference(), "resultData"); function.add(instanceLocal); function.add(originalLocal); - function.add(resultLocal); function.add(originalDataLocal); function.add(dataCopyLocal); + var newExpr = new WasmStructNew(objectStructure); function.getBody().add(new WasmSetLocal(originalLocal, new WasmCast(new WasmGetLocal(instanceLocal), objectStructure.getNonNullReference()))); - function.getBody().add(new WasmSetLocal(resultLocal, new WasmStructNewDefault(objectStructure))); var classValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); - function.getBody().add(new WasmStructSet(objectStructure, new WasmGetLocal(resultLocal), - WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, classValue)); + newExpr.getInitializers().add(classValue); + newExpr.getInitializers().add(new WasmNullConstant(WasmType.Reference.EQ)); var originalDataValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal), WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET); function.getBody().add(new WasmSetLocal(originalDataLocal, originalDataValue)); var originalLength = new WasmArrayLength(new WasmGetLocal(originalDataLocal)); function.getBody().add(new WasmSetLocal(dataCopyLocal, new WasmArrayNewDefault(arrayType, originalLength))); - function.getBody().add(new WasmStructSet(objectStructure, new WasmGetLocal(resultLocal), - WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET, new WasmGetLocal(dataCopyLocal))); + newExpr.getInitializers().add(new WasmGetLocal(dataCopyLocal)); function.getBody().add(new WasmArrayCopy(arrayType, new WasmGetLocal(dataCopyLocal), new WasmInt32Constant(0), arrayType, new WasmGetLocal(originalDataLocal), new WasmInt32Constant(0), new WasmArrayLength(new WasmGetLocal(originalDataLocal)))); - function.getBody().add(new WasmGetLocal(resultLocal)); - + function.getBody().add(newExpr); return function; } @@ -1327,7 +1323,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } } - classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), + classInfo.structure.getFields().add(new WasmField(wasmArray.getNonNullReference().asStorage(), arrayDataFieldName())); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/NonNullVarsCalculator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/NonNullVarsCalculator.java new file mode 100644 index 000000000..0e1a447b9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/NonNullVarsCalculator.java @@ -0,0 +1,124 @@ +/* + * Copyright 2024 konsoletyper. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generate.gc.methods; + +import java.util.Arrays; +import org.teavm.ast.AssignmentStatement; +import org.teavm.ast.BinaryExpr; +import org.teavm.ast.BlockStatement; +import org.teavm.ast.ConditionalExpr; +import org.teavm.ast.ConditionalStatement; +import org.teavm.ast.RecursiveVisitor; +import org.teavm.ast.SwitchStatement; +import org.teavm.ast.TryCatchStatement; +import org.teavm.ast.VariableExpr; +import org.teavm.ast.WhileStatement; + +class NonNullVarsCalculator extends RecursiveVisitor { + private boolean[] nonNullVars; + private boolean[] definedVars; + + public NonNullVarsCalculator(boolean[] nonNullVars) { + this.nonNullVars = nonNullVars; + definedVars = new boolean[nonNullVars.length]; + } + + @Override + public void visit(VariableExpr expr) { + use(expr.getIndex()); + } + + @Override + public void visit(AssignmentStatement statement) { + statement.getRightValue().acceptVisitor(this); + if (statement.getLeftValue() instanceof VariableExpr) { + var index = ((VariableExpr) statement.getLeftValue()).getIndex(); + define(index); + } else if (statement.getLeftValue() != null) { + statement.getLeftValue().acceptVisitor(this); + } + } + + @Override + public void visit(BlockStatement statement) { + runInBlock(() -> super.visit(statement)); + } + + @Override + public void visit(WhileStatement statement) { + runInBlock(() -> super.visit(statement)); + } + + @Override + public void visit(SwitchStatement statement) { + statement.getValue().acceptVisitor(this); + for (var clause : statement.getClauses()) { + runInBlock(() -> visit(clause.getBody())); + } + runInBlock(() -> visit(statement.getDefaultClause())); + } + + @Override + public void visit(TryCatchStatement statement) { + runInBlock(() -> visit(statement.getProtectedBody())); + runInBlock(() -> visit(statement.getHandler())); + } + + @Override + public void visit(ConditionalStatement statement) { + statement.getCondition().acceptVisitor(this); + runInBlock(() -> visit(statement.getConsequent())); + runInBlock(() -> visit(statement.getAlternative())); + } + + @Override + public void visit(ConditionalExpr expr) { + expr.getCondition().acceptVisitor(this); + runInBlock(() -> expr.getConsequent().acceptVisitor(this)); + runInBlock(() -> expr.getAlternative().acceptVisitor(this)); + } + + @Override + public void visit(BinaryExpr expr) { + switch (expr.getOperation()) { + case AND: + case OR: + expr.getFirstOperand().acceptVisitor(this); + runInBlock(() -> expr.getSecondOperand().acceptVisitor(this)); + break; + default: + super.visit(expr); + break; + } + } + + private void runInBlock(Runnable action) { + var oldDefinedVars = definedVars; + definedVars = Arrays.copyOf(definedVars, definedVars.length); + action.run(); + definedVars = oldDefinedVars; + } + + private void use(int index) { + if (!definedVars[index]) { + nonNullVars[index] = false; + } + } + + private void define(int index) { + definedVars[index] = true; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index a0b386c7f..aa7a2fe5f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; +import org.teavm.ast.RegularMethodNode; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; @@ -269,16 +270,29 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { } } + var nonNullableVars = new boolean[ast.getVariables().size()]; + var preciseTypes = new PreciseValueType[ast.getVariables().size()]; for (var i = firstVar; i < ast.getVariables().size(); ++i) { - var localVar = ast.getVariables().get(i); var representative = method.getProgram().variableAt(variableRepresentatives[i]); var inferredType = typeInference.typeOf(representative); if (inferredType == null) { inferredType = new PreciseValueType(ValueType.object("java.lang.Object"), false); } - var type = !inferredType.isArrayUnwrap - ? typeMapper.mapType(inferredType.valueType) - : classInfoProvider.getClassInfo(inferredType.valueType).getArray().getReference(); + preciseTypes[i] = inferredType; + nonNullableVars[i] = inferredType.isArrayUnwrap; + } + calculateNonNullableVars(nonNullableVars, ast); + + for (var i = firstVar; i < ast.getVariables().size(); ++i) { + var localVar = ast.getVariables().get(i); + var inferredType = preciseTypes[i]; + WasmType type; + if (!inferredType.isArrayUnwrap) { + type = typeMapper.mapType(inferredType.valueType); + } else { + var arrayType = classInfoProvider.getClassInfo(inferredType.valueType).getArray(); + type = nonNullableVars[i] ? arrayType.getNonNullReference() : arrayType.getReference(); + } var wasmLocal = new WasmLocal(type, localVar.getName()); function.add(wasmLocal); } @@ -289,6 +303,11 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { visitor.generate(ast.getBody(), function.getBody()); } + private void calculateNonNullableVars(boolean[] nonNullVars, RegularMethodNode ast) { + var calculator = new NonNullVarsCalculator(nonNullVars); + ast.getBody().acceptVisitor(calculator); + } + private void generateNativeMethodBody(MethodHolder method, WasmFunction function) { var importAnnot = method.getAnnotations().get(Import.class.getName()); if (importAnnot == null) { diff --git a/core/src/main/java/org/teavm/model/optimization/RedundantPhiElimination.java b/core/src/main/java/org/teavm/model/optimization/RedundantPhiElimination.java new file mode 100644 index 000000000..46ffad420 --- /dev/null +++ b/core/src/main/java/org/teavm/model/optimization/RedundantPhiElimination.java @@ -0,0 +1,71 @@ +/* + * Copyright 2024 konsoletyper. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model.optimization; + +import org.teavm.model.Program; +import org.teavm.model.Variable; +import org.teavm.model.util.InstructionVariableMapper; + +public class RedundantPhiElimination implements MethodOptimization { + @Override + public boolean optimize(MethodOptimizationContext context, Program program) { + var changed = false; + var map = new int[program.variableCount()]; + for (var i = 0; i < map.length; ++i) { + map[i] = i; + } + for (var block : program.getBasicBlocks()) { + changed |= block.getPhis().removeIf(phi -> { + if (phi.getIncomings().isEmpty()) { + return true; + } + Variable singleInput = null; + for (var incoming : phi.getIncomings()) { + if (incoming.getValue() != phi.getReceiver()) { + if (singleInput == null) { + singleInput = incoming.getValue(); + } else if (singleInput != incoming.getValue()) { + return false; + } + } + } + if (singleInput != null) { + map[phi.getReceiver().getIndex()] = map[singleInput.getIndex()]; + } + return true; + }); + } + if (changed) { + var mapper = new InstructionVariableMapper(v -> program.variableAt(map(map, v.getIndex()))); + for (var block : program.getBasicBlocks()) { + mapper.apply(block); + } + } + return changed; + } + + private static int map(int[] array, int index) { + var result = array[index]; + if (result != index) { + var newResult = map(array, result); + if (newResult != result) { + array[index] = newResult; + } + result = newResult; + } + return result; + } +} diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 0a84ab164..92a7ddcf8 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -88,6 +88,7 @@ import org.teavm.model.optimization.MethodOptimization; import org.teavm.model.optimization.MethodOptimizationContext; import org.teavm.model.optimization.RedundantJumpElimination; import org.teavm.model.optimization.RedundantNullCheckElimination; +import org.teavm.model.optimization.RedundantPhiElimination; import org.teavm.model.optimization.RepeatedFieldReadElimination; import org.teavm.model.optimization.ScalarReplacement; import org.teavm.model.optimization.SystemArrayCopyOptimization; @@ -830,6 +831,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { if (target.needsSystemArrayCopyOptimization()) { optimizations.add(new SystemArrayCopyOptimization()); } + optimizations.add(new RedundantPhiElimination()); return optimizations; }