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();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateArray",
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();
@ -311,10 +313,9 @@ public class WasmTarget implements TeaVMTarget {
classGenerator);
context.addIntrinsic(exceptionHandlingIntrinsic);
WasmGenerator generator = new WasmGenerator(decompiler, classes,
context, classGenerator);
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
module.setMemorySize(64);
module.setMemorySize(128);
generateMethods(classes, context, generator, module);
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);

View File

@ -64,6 +64,8 @@ import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement;
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.WasmIntrinsicManager;
import org.teavm.backend.wasm.model.WasmFunction;
@ -129,12 +131,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private Set<WasmBlock> usedBlocks = new HashSet<>();
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
private WasmLocal stackVariable;
private BinaryWriter binaryWriter;
WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
WasmFunction function, int firstVariable) {
BinaryWriter binaryWriter, WasmFunction function, int firstVariable) {
this.context = context;
this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
this.function = function;
this.firstVariable = firstVariable;
int typeCount = WasmType.values().length;
@ -1128,6 +1132,31 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override
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

View File

@ -18,6 +18,7 @@ package org.teavm.backend.wasm.generate;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.VariableNode;
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.WasmLocal;
import org.teavm.backend.wasm.model.WasmType;
@ -35,13 +36,15 @@ public class WasmGenerator {
private ClassHolderSource classSource;
private WasmGenerationContext context;
private WasmClassGenerator classGenerator;
private BinaryWriter binaryWriter;
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource,
WasmGenerationContext context, WasmClassGenerator classGenerator) {
WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter) {
this.decompiler = decompiler;
this.classSource = classSource;
this.context = context;
this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
}
public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
@ -66,7 +69,7 @@ public class WasmGenerator {
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
}
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function,
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,
firstVariable);
methodAst.getBody().acceptVisitor(visitor);
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.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
@ -209,8 +210,8 @@ public class ExceptionHandlingShadowStackContributor {
private boolean isCallInstruction(Instruction insn) {
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|| insn instanceof ConstructArrayInstruction || insn instanceof CloneArrayInstruction
|| insn instanceof RaiseInstruction) {
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
return true;
} else if (insn instanceof InvokeInstruction) {
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.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
@ -149,6 +150,7 @@ public class GCShadowStackContributor {
}
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|| insn instanceof ConstructMultiArrayInstruction
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
if (insn instanceof InvokeInstruction
&& !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) {

View File

@ -47,6 +47,21 @@ public final class Allocator {
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
public static native void fillZero(Address address, int count);

View File

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

View File

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

View File

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

View File

@ -19,17 +19,17 @@ var Benchmark = function() {
this.canvas = canvas;
this.module = null;
this.line = "";
this.resultTableBody = document.getElementById("result-table-body");
}
Benchmark.prototype.runAll = function() {
load(this, function() { this.module.exports.main(); }.bind(this));
}
};
function tick(benchmark) {
var exports = benchmark.module.exports;
exports.tick();
console.log("tick");
var exception = exports.sys$catchException();
if (exception != null) {
if (exception !== 0) {
console.log("Exception: " + exception);
}
}
@ -49,41 +49,79 @@ var Benchmark = function() {
function load(benchmark, callback) {
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("GET", "teavm-wasm/classes.wasm");
xhr.onreadystatechange = function() {
xhr.onload = function() {
var response = xhr.response;
if (!response) {
return;
}
var canvas = benchmark.canvas;
var importObj = {
runtime: {
currentTimeMillis: currentTimeMillis,
isNaN: isNaN,
isFinite: isFinite,
getNaN: function() { return NaN; },
putchar: function() { putchar(benchmark); }
putchar: function(code) { putchar(benchmark, code); }
},
benchmark: {
performanceTime: function() { return window.performance.now() || 0; },
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) {
console.log("repeatAfter");
setTimeout(tick.bind(benchmark), time);
setTimeout(tick.bind(null, benchmark), time);
},
setupCanvas: function() {
var canvas = benchmark.canvas;
canvas.setFillStyle("white");
context.setStrokeStyle("grey");
canvas.fillStyle = "white";
canvas.strokeStyle = "grey";
canvas.fillRect(0, 0, 600, 600);
canvas.translate(0, 600);
canvas.scale(1, -1);
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,
debug: {
traceMemoryAccess: function(callSite, address) {
@ -94,11 +132,13 @@ var Benchmark = function() {
}
}
};
benchmark.module = Wasm.instantiateModule(new Uint8Array(response), importObj)
callback();
WebAssembly.compile(response).then(function(module) {
benchmark.module = new WebAssembly.Instance(module, importObj);
callback();
});
};
xhr.send();
}
return Benchmark;
}();
}();