wasm gc: use non-null array storage type

This commit is contained in:
Alexey Andreev 2024-09-26 20:40:28 +02:00
parent 1d81b7004f
commit 33f4537f43
5 changed files with 228 additions and 16 deletions

View File

@ -937,38 +937,34 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
module.functions.add(function); module.functions.add(function);
var instanceLocal = new WasmLocal(standardClasses.objectClass().getType(), "instance"); var instanceLocal = new WasmLocal(standardClasses.objectClass().getType(), "instance");
var originalLocal = new WasmLocal(objectStructure.getReference(), "original"); var originalLocal = new WasmLocal(objectStructure.getReference(), "original");
var resultLocal = new WasmLocal(objectStructure.getReference(), "result"); var originalDataLocal = new WasmLocal(arrayType.getNonNullReference(), "originalData");
var originalDataLocal = new WasmLocal(arrayType.getReference(), "originalData"); var dataCopyLocal = new WasmLocal(arrayType.getNonNullReference(), "resultData");
var dataCopyLocal = new WasmLocal(arrayType.getReference(), "resultData");
function.add(instanceLocal); function.add(instanceLocal);
function.add(originalLocal); function.add(originalLocal);
function.add(resultLocal);
function.add(originalDataLocal); function.add(originalDataLocal);
function.add(dataCopyLocal); function.add(dataCopyLocal);
var newExpr = new WasmStructNew(objectStructure);
function.getBody().add(new WasmSetLocal(originalLocal, function.getBody().add(new WasmSetLocal(originalLocal,
new WasmCast(new WasmGetLocal(instanceLocal), objectStructure.getNonNullReference()))); new WasmCast(new WasmGetLocal(instanceLocal), objectStructure.getNonNullReference())));
function.getBody().add(new WasmSetLocal(resultLocal, new WasmStructNewDefault(objectStructure)));
var classValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal), var classValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
function.getBody().add(new WasmStructSet(objectStructure, new WasmGetLocal(resultLocal), newExpr.getInitializers().add(classValue);
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, classValue)); newExpr.getInitializers().add(new WasmNullConstant(WasmType.Reference.EQ));
var originalDataValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal), var originalDataValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal),
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET); WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
function.getBody().add(new WasmSetLocal(originalDataLocal, originalDataValue)); function.getBody().add(new WasmSetLocal(originalDataLocal, originalDataValue));
var originalLength = new WasmArrayLength(new WasmGetLocal(originalDataLocal)); var originalLength = new WasmArrayLength(new WasmGetLocal(originalDataLocal));
function.getBody().add(new WasmSetLocal(dataCopyLocal, new WasmArrayNewDefault(arrayType, originalLength))); function.getBody().add(new WasmSetLocal(dataCopyLocal, new WasmArrayNewDefault(arrayType, originalLength)));
function.getBody().add(new WasmStructSet(objectStructure, new WasmGetLocal(resultLocal), newExpr.getInitializers().add(new WasmGetLocal(dataCopyLocal));
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET, new WasmGetLocal(dataCopyLocal)));
function.getBody().add(new WasmArrayCopy(arrayType, new WasmGetLocal(dataCopyLocal), function.getBody().add(new WasmArrayCopy(arrayType, new WasmGetLocal(dataCopyLocal),
new WasmInt32Constant(0), arrayType, new WasmGetLocal(originalDataLocal), new WasmInt32Constant(0), arrayType, new WasmGetLocal(originalDataLocal),
new WasmInt32Constant(0), new WasmArrayLength(new WasmGetLocal(originalDataLocal)))); new WasmInt32Constant(0), new WasmArrayLength(new WasmGetLocal(originalDataLocal))));
function.getBody().add(new WasmGetLocal(resultLocal)); function.getBody().add(newExpr);
return function; 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())); arrayDataFieldName()));
} }

View File

@ -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;
}
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; 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) { for (var i = firstVar; i < ast.getVariables().size(); ++i) {
var localVar = ast.getVariables().get(i);
var representative = method.getProgram().variableAt(variableRepresentatives[i]); var representative = method.getProgram().variableAt(variableRepresentatives[i]);
var inferredType = typeInference.typeOf(representative); var inferredType = typeInference.typeOf(representative);
if (inferredType == null) { if (inferredType == null) {
inferredType = new PreciseValueType(ValueType.object("java.lang.Object"), false); inferredType = new PreciseValueType(ValueType.object("java.lang.Object"), false);
} }
var type = !inferredType.isArrayUnwrap preciseTypes[i] = inferredType;
? typeMapper.mapType(inferredType.valueType) nonNullableVars[i] = inferredType.isArrayUnwrap;
: classInfoProvider.getClassInfo(inferredType.valueType).getArray().getReference(); }
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()); var wasmLocal = new WasmLocal(type, localVar.getName());
function.add(wasmLocal); function.add(wasmLocal);
} }
@ -289,6 +303,11 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
visitor.generate(ast.getBody(), function.getBody()); 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) { private void generateNativeMethodBody(MethodHolder method, WasmFunction function) {
var importAnnot = method.getAnnotations().get(Import.class.getName()); var importAnnot = method.getAnnotations().get(Import.class.getName());
if (importAnnot == null) { if (importAnnot == null) {

View File

@ -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;
}
}

View File

@ -88,6 +88,7 @@ import org.teavm.model.optimization.MethodOptimization;
import org.teavm.model.optimization.MethodOptimizationContext; import org.teavm.model.optimization.MethodOptimizationContext;
import org.teavm.model.optimization.RedundantJumpElimination; import org.teavm.model.optimization.RedundantJumpElimination;
import org.teavm.model.optimization.RedundantNullCheckElimination; import org.teavm.model.optimization.RedundantNullCheckElimination;
import org.teavm.model.optimization.RedundantPhiElimination;
import org.teavm.model.optimization.RepeatedFieldReadElimination; import org.teavm.model.optimization.RepeatedFieldReadElimination;
import org.teavm.model.optimization.ScalarReplacement; import org.teavm.model.optimization.ScalarReplacement;
import org.teavm.model.optimization.SystemArrayCopyOptimization; import org.teavm.model.optimization.SystemArrayCopyOptimization;
@ -830,6 +831,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (target.needsSystemArrayCopyOptimization()) { if (target.needsSystemArrayCopyOptimization()) {
optimizations.add(new SystemArrayCopyOptimization()); optimizations.add(new SystemArrayCopyOptimization());
} }
optimizations.add(new RedundantPhiElimination());
return optimizations; return optimizations;
} }