mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
WASM: support multianewarray, fix jbox2d benchmark for WebAssembly
This commit is contained in:
parent
84628b7008
commit
0041130d00
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}();
|
||||
}();
|
Loading…
Reference in New Issue
Block a user