WASM: support multianewarray, fix jbox2d benchmark for WebAssembly

This commit is contained in:
Alexey Andreev 2016-10-06 15:49:58 +03:00
parent 84628b7008
commit 0041130d00
10 changed files with 123 additions and 29 deletions

View File

@ -225,6 +225,8 @@ public class WasmTarget implements TeaVMTarget {
RuntimeClass.class, Address.class), null).use(); RuntimeClass.class, Address.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateArray", dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateArray",
RuntimeClass.class, int.class, Address.class), null).use(); RuntimeClass.class, int.class, Address.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
RuntimeClass.class, Address.class, int.class, RuntimeArray.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use(); dependencyChecker.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
@ -311,10 +313,9 @@ public class WasmTarget implements TeaVMTarget {
classGenerator); classGenerator);
context.addIntrinsic(exceptionHandlingIntrinsic); context.addIntrinsic(exceptionHandlingIntrinsic);
WasmGenerator generator = new WasmGenerator(decompiler, classes, WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
context, classGenerator);
module.setMemorySize(64); module.setMemorySize(128);
generateMethods(classes, context, generator, module); generateMethods(classes, context, generator, module);
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites()); exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
generateIsSupertypeFunctions(tagRegistry, module, classGenerator); generateIsSupertypeFunctions(tagRegistry, module, classGenerator);

View File

@ -64,6 +64,8 @@ import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.binary.DataPrimitives;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
@ -129,12 +131,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private Set<WasmBlock> usedBlocks = new HashSet<>(); private Set<WasmBlock> usedBlocks = new HashSet<>();
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>(); private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
private WasmLocal stackVariable; private WasmLocal stackVariable;
private BinaryWriter binaryWriter;
WasmExpression result; WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
WasmFunction function, int firstVariable) { BinaryWriter binaryWriter, WasmFunction function, int firstVariable) {
this.context = context; this.context = context;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
this.function = function; this.function = function;
this.firstVariable = firstVariable; this.firstVariable = firstVariable;
int typeCount = WasmType.values().length; int typeCount = WasmType.values().length;
@ -1128,6 +1132,31 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(NewMultiArrayExpr expr) { public void visit(NewMultiArrayExpr expr) {
ValueType type = expr.getType();
WasmBlock block = new WasmBlock(false);
int dimensionList = -1;
for (Expr dimension : expr.getDimensions()) {
int dimensionAddress = binaryWriter.append(DataPrimitives.INT.createValue());
if (dimensionList < 0) {
dimensionList = dimensionAddress;
}
accept(dimension);
block.getBody().add(new WasmStoreInt32(4, new WasmInt32Constant(dimensionAddress), result,
WasmInt32Subtype.INT32));
}
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocateMultiArray",
RuntimeClass.class, Address.class, int.class, RuntimeArray.class));
WasmCall call = new WasmCall(allocName);
call.getArguments().add(new WasmInt32Constant(classPointer));
call.getArguments().add(new WasmInt32Constant(dimensionList));
call.getArguments().add(new WasmInt32Constant(expr.getDimensions().size()));
call.setLocation(expr.getLocation());
block.getBody().add(call);
result = block;
} }
@Override @Override

View File

@ -18,6 +18,7 @@ package org.teavm.backend.wasm.generate;
import org.teavm.ast.RegularMethodNode; import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode; import org.teavm.ast.VariableNode;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -35,13 +36,15 @@ public class WasmGenerator {
private ClassHolderSource classSource; private ClassHolderSource classSource;
private WasmGenerationContext context; private WasmGenerationContext context;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private BinaryWriter binaryWriter;
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource,
WasmGenerationContext context, WasmClassGenerator classGenerator) { WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter) {
this.decompiler = decompiler; this.decompiler = decompiler;
this.classSource = classSource; this.classSource = classSource;
this.context = context; this.context = context;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
} }
public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) { public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
@ -66,7 +69,7 @@ public class WasmGenerator {
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType())); function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
} }
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,
firstVariable); firstVariable);
methodAst.getBody().acceptVisitor(visitor); methodAst.getBody().acceptVisitor(visitor);
function.getBody().add(visitor.result); function.getBody().add(visitor.result);

View File

@ -37,6 +37,7 @@ import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction; import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction; import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction; import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction; import org.teavm.model.instructions.FloatConstantInstruction;
@ -209,8 +210,8 @@ public class ExceptionHandlingShadowStackContributor {
private boolean isCallInstruction(Instruction insn) { private boolean isCallInstruction(Instruction insn) {
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|| insn instanceof ConstructArrayInstruction || insn instanceof CloneArrayInstruction || insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|| insn instanceof RaiseInstruction) { || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
return true; return true;
} else if (insn instanceof InvokeInstruction) { } else if (insn instanceof InvokeInstruction) {
return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod()); return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod());

View File

@ -41,6 +41,7 @@ import org.teavm.model.Variable;
import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction; import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction; import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.InitClassInstruction; import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
@ -149,6 +150,7 @@ public class GCShadowStackContributor {
} }
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction || insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|| insn instanceof ConstructMultiArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) { || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
if (insn instanceof InvokeInstruction if (insn instanceof InvokeInstruction
&& !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) { && !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) {

View File

@ -47,6 +47,21 @@ public final class Allocator {
return result; return result;
} }
public static RuntimeArray allocateMultiArray(RuntimeClass tag, Address dimensions, int dimensionCount) {
int size = dimensions.getInt();
RuntimeArray array = allocateArray(tag, dimensions.getInt()).toStructure();
if (dimensionCount > 1) {
Address arrayData = Structure.add(RuntimeArray.class, array, 1).toAddress();
arrayData = Address.align(arrayData, Address.sizeOf());
for (int i = 0; i < size; ++i) {
RuntimeArray innerArray = allocateMultiArray(tag.itemType, dimensions.add(4), dimensionCount - 1);
arrayData.putAddress(innerArray.toAddress());
arrayData = arrayData.add(Address.sizeOf());
}
}
return array;
}
@Unmanaged @Unmanaged
public static native void fillZero(Address address, int count); public static native void fillZero(Address address, int count);

View File

@ -103,6 +103,9 @@
<resource> <resource>
<directory>${project.build.directory}/generated/js</directory> <directory>${project.build.directory}/generated/js</directory>
</resource> </resource>
<resource>
<directory>${project.build.directory}/generated/wasm</directory>
</resource>
</webResources> </webResources>
</configuration> </configuration>
</plugin> </plugin>
@ -126,6 +129,7 @@
<excludes> <excludes>
<exclude>**/gwt/*</exclude> <exclude>**/gwt/*</exclude>
<exclude>**/teavm/*</exclude> <exclude>**/teavm/*</exclude>
<exclude>**/benchmark/jvm/*</exclude>
</excludes> </excludes>
</configuration> </configuration>
</execution> </execution>

View File

@ -30,7 +30,7 @@ public final class WasmBenchmarkStarter {
private static Scene scene = new Scene(); private static Scene scene = new Scene();
private static int currentSecond; private static int currentSecond;
private static long startMillisecond; private static long startMillisecond;
private static int timeSpentCalculating; private static double timeSpentCalculating;
private WasmBenchmarkStarter() { private WasmBenchmarkStarter() {
} }
@ -43,17 +43,15 @@ public final class WasmBenchmarkStarter {
@Export(name = "tick") @Export(name = "tick")
public static void tick() { public static void tick() {
double start = performanceTime(); double start = performanceTime();
System.out.println("About to calculate frame");
scene.calculate(); scene.calculate();
System.out.println("Frame calculated successfully");
double end = performanceTime(); double end = performanceTime();
int second = (int) ((System.currentTimeMillis() - startMillisecond) / 1000); int second = (int) ((System.currentTimeMillis() - startMillisecond) / 1000);
if (second > currentSecond) { if (second > currentSecond) {
reportPerformance(second, timeSpentCalculating); reportPerformance(second, (int) timeSpentCalculating);
timeSpentCalculating = 0; timeSpentCalculating = 0;
currentSecond = second; currentSecond = second;
} }
timeSpentCalculating += (int) (end - start); timeSpentCalculating += end - start;
render(); render();
repeatAfter(scene.timeUntilNextStep()); repeatAfter(scene.timeUntilNextStep());
} }

View File

@ -25,6 +25,7 @@
<li><a href="teavm.html">TeaVM</a></li> <li><a href="teavm.html">TeaVM</a></li>
<li><a href="gwt.html">GWT</a></li> <li><a href="gwt.html">GWT</a></li>
<li><a href="bck2brwsr.html">Bck2Brwsr VM</a></li> <li><a href="bck2brwsr.html">Bck2Brwsr VM</a></li>
<li><a href="teavm-wasm.html">TeaVM (experimental WebAssembly backend)</a></li>
</ul> </ul>
</body> </body>
</html> </html>

View File

@ -19,17 +19,17 @@ var Benchmark = function() {
this.canvas = canvas; this.canvas = canvas;
this.module = null; this.module = null;
this.line = ""; this.line = "";
this.resultTableBody = document.getElementById("result-table-body");
} }
Benchmark.prototype.runAll = function() { Benchmark.prototype.runAll = function() {
load(this, function() { this.module.exports.main(); }.bind(this)); load(this, function() { this.module.exports.main(); }.bind(this));
} };
function tick(benchmark) { function tick(benchmark) {
var exports = benchmark.module.exports; var exports = benchmark.module.exports;
exports.tick(); exports.tick();
console.log("tick");
var exception = exports.sys$catchException(); var exception = exports.sys$catchException();
if (exception != null) { if (exception !== 0) {
console.log("Exception: " + exception); console.log("Exception: " + exception);
} }
} }
@ -49,41 +49,79 @@ var Benchmark = function() {
function load(benchmark, callback) { function load(benchmark, callback) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("GET", "teavm-wasm/classes.wasm"); xhr.open("GET", "teavm-wasm/classes.wasm");
xhr.onreadystatechange = function() { xhr.onload = function() {
var response = xhr.response; var response = xhr.response;
if (!response) { if (!response) {
return; return;
} }
var canvas = benchmark.canvas;
var importObj = { var importObj = {
runtime: { runtime: {
currentTimeMillis: currentTimeMillis, currentTimeMillis: currentTimeMillis,
isNaN: isNaN, isNaN: isNaN,
isFinite: isFinite, isFinite: isFinite,
getNaN: function() { return NaN; }, getNaN: function() { return NaN; },
putchar: function() { putchar(benchmark); } putchar: function(code) { putchar(benchmark, code); }
}, },
benchmark: { benchmark: {
performanceTime: function() { return window.performance.now() || 0; }, performanceTime: function() { return window.performance.now() || 0; },
reportPerformance: function(second, timeSpentComputing) { reportPerformance: function(second, timeSpentComputing) {
console.log("Second: " + second + ", time: " + timeSpentComputing); var row = document.createElement("tr");
benchmark.resultTableBody.appendChild(row);
var secondCell = document.createElement("td");
row.appendChild(secondCell);
secondCell.appendChild(document.createTextNode(second.toString()));
var timeCell = document.createElement("td");
row.appendChild(timeCell);
timeCell.appendChild(document.createTextNode(timeSpentComputing.toString()));
}, },
repeatAfter: function(time) { repeatAfter: function(time) {
console.log("repeatAfter"); setTimeout(tick.bind(null, benchmark), time);
setTimeout(tick.bind(benchmark), time);
}, },
setupCanvas: function() { setupCanvas: function() {
var canvas = benchmark.canvas; canvas.fillStyle = "white";
canvas.setFillStyle("white"); canvas.strokeStyle = "grey";
context.setStrokeStyle("grey");
canvas.fillRect(0, 0, 600, 600); canvas.fillRect(0, 0, 600, 600);
canvas.translate(0, 600); canvas.translate(0, 600);
canvas.scale(1, -1); canvas.scale(1, -1);
canvas.scale(100, 100); canvas.scale(100, 100);
canvas.setLineWidth(0.01); canvas.lineWidth = 0.01;
}
},
canvas: {
save: function() {
canvas.save();
},
restore: function() {
canvas.restore();
},
beginPath: function() {
canvas.beginPath();
},
closePath: function() {
canvas.closePath();
},
stroke: function() {
canvas.stroke();
},
moveTo: function(x, y) {
canvas.moveTo(x, y);
},
lineTo: function(x, y) {
canvas.lineTo(x, y);
},
translate: function(x, y) {
canvas.translate(x, y);
},
rotate: function(angle) {
canvas.rotate(angle);
},
arc: function(cx, cy, radius, startAngle, endAngle, counterClockwise) {
canvas.arc(cx, cy, radius, startAngle, endAngle, counterClockwise);
} }
}, },
canvas: benchmark.canvas,
math: Math, math: Math,
debug: { debug: {
traceMemoryAccess: function(callSite, address) { traceMemoryAccess: function(callSite, address) {
@ -94,8 +132,10 @@ var Benchmark = function() {
} }
} }
}; };
benchmark.module = Wasm.instantiateModule(new Uint8Array(response), importObj) WebAssembly.compile(response).then(function(module) {
benchmark.module = new WebAssembly.Instance(module, importObj);
callback(); callback();
});
}; };
xhr.send(); xhr.send();
} }