mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-21 01:00:54 +08:00
wasm gc: sort types, functions and globals in usage count order to reduce binary size
This commit is contained in:
parent
86e8cfd0db
commit
7e622d8bc7
@ -17,6 +17,7 @@ package org.teavm.backend.wasm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -29,6 +30,7 @@ import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsics;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.optimization.WasmUsageCounter;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||
@ -209,6 +211,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||
var binaryWriter = new WasmBinaryWriter();
|
||||
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
|
||||
null, null, null, null, WasmBinaryStatsCollector.EMPTY);
|
||||
optimizeIndexes(module);
|
||||
module.prepareForRendering();
|
||||
binaryRenderer.render(module);
|
||||
var data = binaryWriter.getData();
|
||||
@ -220,6 +223,14 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||
}
|
||||
}
|
||||
|
||||
private void optimizeIndexes(WasmModule module) {
|
||||
var usageCounter = new WasmUsageCounter();
|
||||
usageCounter.applyToModule(module);
|
||||
module.functions.sort(Comparator.comparingInt(f -> -usageCounter.usages(f)));
|
||||
module.globals.sort(Comparator.comparingInt(g -> -usageCounter.usages(g)));
|
||||
module.types.sort(Comparator.comparingInt(t -> -usageCounter.usages(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSystemArrayCopyOptimization() {
|
||||
return false;
|
||||
|
@ -17,6 +17,7 @@ package org.teavm.backend.wasm.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
@ -104,4 +105,9 @@ public class WasmCollection<T extends WasmEntity> implements Iterable<T> {
|
||||
public Stream<T> stream() {
|
||||
return readonlyItems.stream();
|
||||
}
|
||||
|
||||
public void sort(Comparator<T> comparator) {
|
||||
items.sort(comparator);
|
||||
indexesInvalid = true;
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,13 @@ package org.teavm.backend.wasm.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphUtils;
|
||||
|
||||
@ -94,9 +98,44 @@ public class WasmModule {
|
||||
}
|
||||
|
||||
public void prepareForRendering() {
|
||||
prepareGlobals();
|
||||
prepareTypes();
|
||||
}
|
||||
|
||||
private void prepareGlobals() {
|
||||
var sorting = new GlobalSorting();
|
||||
sorting.sort(globals);
|
||||
globals.clear();
|
||||
for (var global : sorting.sorted) {
|
||||
globals.add(global);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GlobalSorting extends WasmDefaultExpressionVisitor {
|
||||
List<WasmGlobal> sorted = new ArrayList<>();
|
||||
private Set<WasmGlobal> visited = new HashSet<>();
|
||||
|
||||
void sort(Iterable<WasmGlobal> globals) {
|
||||
for (var global : globals) {
|
||||
add(global);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(WasmGlobal global) {
|
||||
if (!visited.add(global)) {
|
||||
return;
|
||||
}
|
||||
global.getInitialValue().acceptVisitor(this);
|
||||
sorted.add(global);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmGetGlobal expression) {
|
||||
super.visit(expression);
|
||||
add(expression.getGlobal());
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareTypes() {
|
||||
var typeGraph = WasmTypeGraphBuilder.buildTypeGraph(this, types, types.size());
|
||||
var sccs = GraphUtils.findStronglyConnectedComponents(typeGraph);
|
||||
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2024 Alexey Andreev.
|
||||
*
|
||||
* 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.optimization;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import org.teavm.backend.wasm.model.WasmArray;
|
||||
import org.teavm.backend.wasm.model.WasmCompositeType;
|
||||
import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmFunctionType;
|
||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmStructure;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayCopy;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed;
|
||||
import org.teavm.backend.wasm.model.expression.WasmArraySet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCallReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCast;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCastBranch;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructNew;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||
import org.teavm.backend.wasm.model.expression.WasmTest;
|
||||
|
||||
public class WasmUsageCounter extends WasmDefaultExpressionVisitor implements WasmCompositeTypeVisitor {
|
||||
private ObjectIntMap<WasmFunction> usagesByFunction = new ObjectIntHashMap<>();
|
||||
private ObjectIntMap<WasmGlobal> usagesByGlobals = new ObjectIntHashMap<>();
|
||||
private ObjectIntMap<WasmCompositeType> usagesByTypes = new ObjectIntHashMap<>();
|
||||
|
||||
public void applyToModule(WasmModule module) {
|
||||
for (var type : module.types) {
|
||||
type.acceptVisitor(this);
|
||||
}
|
||||
for (var function : module.functions) {
|
||||
for (var part : function.getBody()) {
|
||||
part.acceptVisitor(this);
|
||||
}
|
||||
addUsage(function.getType());
|
||||
}
|
||||
for (var global : module.globals) {
|
||||
global.getInitialValue().acceptVisitor(this);
|
||||
addUsage(global.getType());
|
||||
}
|
||||
}
|
||||
|
||||
public int usages(WasmFunction function) {
|
||||
return usagesByFunction.get(function);
|
||||
}
|
||||
|
||||
public int usages(WasmGlobal global) {
|
||||
return usagesByGlobals.get(global);
|
||||
}
|
||||
|
||||
public int usages(WasmCompositeType type) {
|
||||
return usagesByTypes.get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructure type) {
|
||||
for (var field : type.getFields()) {
|
||||
addUsage(field.getUnpackedType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArray type) {
|
||||
addUsage(type.getElementType().asUnpackedType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmFunctionType type) {
|
||||
addUsage(type.getReturnType());
|
||||
for (var param : type.getParameterTypes()) {
|
||||
addUsage(param);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmCall expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getFunction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmFunctionReference expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getFunction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmGetGlobal expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getGlobal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmSetGlobal expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getGlobal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmBlock expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmCastBranch expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getSourceType());
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmCallReference expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmIndirectCall expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmCast expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getTargetType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmTest expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getTestType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNew expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructNewDefault expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructGet expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmStructSet expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArrayNewDefault expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArrayNewFixed expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArrayGet expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArraySet expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WasmArrayCopy expression) {
|
||||
super.visit(expression);
|
||||
addUsage(expression.getSourceArrayType());
|
||||
addUsage(expression.getTargetArrayType());
|
||||
}
|
||||
|
||||
private void addUsage(WasmType type) {
|
||||
if (type instanceof WasmType.CompositeReference) {
|
||||
addUsage(((WasmType.CompositeReference) type).composite);
|
||||
}
|
||||
}
|
||||
|
||||
private void addUsage(WasmFunction function) {
|
||||
var index = usagesByFunction.indexOf(function);
|
||||
if (index < 0) {
|
||||
usagesByFunction.put(function, 1);
|
||||
} else {
|
||||
usagesByFunction.indexReplace(index, usagesByFunction.indexGet(index) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void addUsage(WasmGlobal function) {
|
||||
var index = usagesByGlobals.indexOf(function);
|
||||
if (index < 0) {
|
||||
usagesByGlobals.put(function, 1);
|
||||
} else {
|
||||
usagesByGlobals.indexReplace(index, usagesByGlobals.indexGet(index) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void addUsage(WasmCompositeType type) {
|
||||
var index = usagesByTypes.indexOf(type);
|
||||
if (index < 0) {
|
||||
usagesByTypes.put(type, 1);
|
||||
} else {
|
||||
usagesByTypes.indexReplace(index, usagesByTypes.indexGet(index) + 1);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user