Further work on WASM binary format generator

This commit is contained in:
Alexey Andreev 2016-09-01 19:34:10 +03:00
parent c70ba8f16d
commit d0def96dba
7 changed files with 1257 additions and 27 deletions

View File

@ -17,8 +17,6 @@ package org.teavm.backend.wasm;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -66,7 +64,8 @@ import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.patches.ClassPatch;
import org.teavm.backend.wasm.render.WasmCRenderer;
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener;
@ -272,14 +271,13 @@ public class WasmTarget implements TeaVMTarget {
module.getFunctionTable().add(module.getFunctions().get(function));
}
WasmCRenderer renderer = new WasmCRenderer();
renderer.setOutputLineNumbers(debugging);
WasmBinaryWriter writer = new WasmBinaryWriter();
WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer);
renderer.render(module);
try {
Writer writer = new OutputStreamWriter(output, "UTF-8");
writer.append(renderer.toString());
writer.flush();
output.write(writer.getData());
output.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@ -0,0 +1,290 @@
/*
* Copyright 2016 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.render;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmMemorySegment;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression;
public class WasmBinaryRenderer {
private WasmBinaryWriter output;
private List<WasmSignature> signatures = new ArrayList<>();
private Map<WasmSignature, Integer> signatureIndexes = new HashMap<>();
private Map<String, Integer> importIndexes = new HashMap<>();
private Map<String, Integer> functionIndexes = new HashMap<>();
public WasmBinaryRenderer(WasmBinaryWriter output) {
this.output = output;
}
public void render(WasmModule module) {
output.writeInt32(0x6d736100);
output.writeInt32(11);
renderSignatures(module);
renderImports(module);
renderFunctions(module);
renderTable(module);
renderMemory(module);
renderExport(module);
renderStart(module);
renderCode(module);
renderData(module);
}
private void renderSignatures(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter();
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(this::registerSignature);
for (WasmFunction function : module.getFunctions().values()) {
registerSignature(WasmSignature.fromFunction(function));
for (WasmExpression part : function.getBody()) {
part.acceptVisitor(signatureCollector);
}
}
section.writeLEB(signatures.size());
for (WasmSignature signature : signatures) {
section.writeByte(0x40);
section.writeLEB(signature.types.length - 1);
for (int i = 1; i < signature.types.length; ++i) {
section.writeType(signature.types[i]);
}
if (signature.types[0] != null) {
section.writeByte(1);
section.writeType(signature.types[0]);
} else {
section.writeByte(0);
}
}
writeSection("type", section.getData());
}
private void renderImports(WasmModule module) {
int index = 0;
List<WasmFunction> functions = new ArrayList<>();
for (WasmFunction function : module.getFunctions().values()) {
if (function.getImportName() == null) {
continue;
}
functions.add(function);
importIndexes.put(function.getName(), index++);
}
if (functions.isEmpty()) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(functions.size());
for (WasmFunction function : functions) {
WasmSignature signature = WasmSignature.fromFunction(function);
section.writeLEB(signatureIndexes.get(signature));
String moduleName = function.getImportModule();
if (moduleName == null) {
moduleName = "";
}
section.writeAsciiString(moduleName);
section.writeAsciiString(function.getImportName());
}
writeSection("import", section.getData());
}
private void renderFunctions(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter();
int index = 0;
List<WasmFunction> functions = module.getFunctions().values().stream()
.filter(function -> function.getImportName() == null)
.collect(Collectors.toList());
for (WasmFunction function : functions) {
functionIndexes.put(function.getName(), index++);
}
section.writeLEB(functions.size());
for (WasmFunction function : functions) {
WasmSignature signature = WasmSignature.fromFunction(function);
section.writeLEB(signatureIndexes.get(signature));
}
writeSection("function", section.getData());
}
private void renderTable(WasmModule module) {
if (module.getFunctionTable().isEmpty()) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(module.getFunctionTable().size());
for (WasmFunction function : module.getFunctionTable()) {
section.writeLEB(functionIndexes.get(function.getName()));
}
writeSection("table", section.getData());
}
private void renderMemory(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(module.getMemorySize());
section.writeLEB(module.getMemorySize());
section.writeByte(0);
writeSection("memory", section.getData());
}
private void renderExport(WasmModule module) {
List<WasmFunction> functions = module.getFunctions().values().stream()
.filter(function -> function.getExportName() != null)
.collect(Collectors.toList());
if (functions.isEmpty()) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(functions.size());
for (WasmFunction function : functions) {
section.writeLEB(functionIndexes.get(function.getName()));
section.writeAsciiString(function.getExportName());
}
writeSection("export", section.getData());
}
private void renderStart(WasmModule module) {
if (module.getStartFunction() == null) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(functionIndexes.get(module.getStartFunction().getName()));
writeSection("start", section.getData());
}
private void renderCode(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter();
List<WasmFunction> functions = module.getFunctions().values().stream()
.filter(function -> function.getImportName() == null)
.collect(Collectors.toList());
section.writeLEB(functions.size());
for (WasmFunction function : functions) {
if (function.getLocalVariables().isEmpty()) {
section.writeLEB(0);
} else {
List<LocalEntry> localEntries = new ArrayList<>();
LocalEntry currentEntry = new LocalEntry(function.getLocalVariables().get(0).getType());
for (int i = 1; i < function.getLocalVariables().size(); ++i) {
WasmType type = function.getLocalVariables().get(i).getType();
if (currentEntry.type == type) {
currentEntry.count++;
} else {
localEntries.add(currentEntry);
currentEntry = new LocalEntry(type);
}
}
localEntries.add(currentEntry);
section.writeLEB(localEntries.size());
for (LocalEntry entry : localEntries) {
section.writeLEB(entry.count);
section.writeType(entry.type);
}
}
System.out.println(function.getName() + ": " + (output.getPosition() + section.getPosition()));
byte[] body = renderFunction(function);
section.writeLEB(body.length);
section.writeBytes(body);
}
writeSection("code", section.getData());
}
private byte[] renderFunction(WasmFunction function) {
WasmBinaryWriter code = new WasmBinaryWriter();
WasmBinaryRenderingVisitor visitor = new WasmBinaryRenderingVisitor(code, functionIndexes, importIndexes,
signatureIndexes);
for (WasmExpression part : function.getBody()) {
part.acceptVisitor(visitor);
}
return code.getData();
}
private void renderData(WasmModule module) {
if (module.getSegments().isEmpty()) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(module.getSegments().size());
for (WasmMemorySegment segment : module.getSegments()) {
section.writeLEB(segment.getOffset());
section.writeLEB(segment.getLength());
int chunkSize = 65536;
for (int i = 0; i < segment.getLength(); i += chunkSize) {
int next = Math.min(i + chunkSize, segment.getLength());
section.writeBytes(segment.getData(i, next - i));
}
}
writeSection("data", section.getData());
}
static class LocalEntry {
WasmType type;
int count = 1;
public LocalEntry(WasmType type) {
this.type = type;
}
}
private void registerSignature(WasmSignature signature) {
signatureIndexes.computeIfAbsent(signature, key -> {
int result = signatures.size();
signatures.add(key);
return result;
});
}
private void writeSection(String id, byte[] data) {
output.writeAsciiString(id);
output.writeLEB(data.length);
output.writeBytes(data);
}
}

View File

@ -0,0 +1,784 @@
/*
* Copyright 2016 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.render;
import java.util.HashMap;
import java.util.Map;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat64;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private WasmBinaryWriter writer;
private Map<String, Integer> functionIndexes;
private Map<String, Integer> importedIndexes;
private Map<WasmSignature, Integer> signatureIndexes;
private int depth;
private Map<WasmBlock, Integer> blockDepths = new HashMap<>();
WasmBinaryRenderingVisitor(WasmBinaryWriter writer, Map<String, Integer> functionIndexes,
Map<String, Integer> importedIndexes, Map<WasmSignature, Integer> signatureIndexes) {
this.writer = writer;
this.functionIndexes = functionIndexes;
this.importedIndexes = importedIndexes;
this.signatureIndexes = signatureIndexes;
}
@Override
public void visit(WasmBlock expression) {
++depth;
blockDepths.put(expression, depth);
writer.writeByte(0x01);
for (WasmExpression part : expression.getBody()) {
part.acceptVisitor(this);
}
writer.writeByte(0x0F);
blockDepths.remove(expression);
--depth;
}
@Override
public void visit(WasmBranch expression) {
if (expression.getResult() != null) {
expression.getResult().acceptVisitor(this);
}
expression.getCondition().acceptVisitor(this);
writer.writeByte(0x07);
writer.writeByte(expression.getResult() != null ? 1 : 0);
writeLabel(expression.getTarget());
}
@Override
public void visit(WasmBreak expression) {
if (expression.getResult() != null) {
expression.getResult().acceptVisitor(this);
}
writer.writeByte(0x06);
writer.writeByte(expression.getResult() != null ? 1 : 0);
writeLabel(expression.getTarget());
}
@Override
public void visit(WasmSwitch expression) {
expression.getSelector().acceptVisitor(this);
writer.writeByte(0x08);
writer.writeByte(0);
writer.writeLEB(expression.getTargets().size());
expression.getTargets().forEach(this::writeLabel);
writeLabel(expression.getDefaultTarget());
}
@Override
public void visit(WasmConditional expression) {
expression.getCondition().acceptVisitor(this);
writer.writeByte(0x03);
++depth;
blockDepths.put(expression.getThenBlock(), depth);
for (WasmExpression part : expression.getThenBlock().getBody()) {
part.acceptVisitor(this);
}
blockDepths.remove(expression.getThenBlock());
if (!expression.getElseBlock().getBody().isEmpty()) {
writer.writeByte(0x04);
blockDepths.put(expression.getElseBlock(), depth);
for (WasmExpression part : expression.getElseBlock().getBody()) {
part.acceptVisitor(this);
}
blockDepths.remove(expression.getElseBlock());
}
--depth;
writer.writeByte(0x0F);
}
@Override
public void visit(WasmReturn expression) {
if (expression.getValue() != null) {
expression.getValue().acceptVisitor(this);
}
writer.writeByte(0x09);
writer.writeByte(expression.getValue() != null ? 1 : 0);
}
@Override
public void visit(WasmUnreachable expression) {
writer.writeByte(0x0A);
}
@Override
public void visit(WasmInt32Constant expression) {
writer.writeByte(0x10);
writer.writeSignedLEB(expression.getValue());
}
@Override
public void visit(WasmInt64Constant expression) {
writer.writeByte(0x11);
writer.writeSignedLEB(expression.getValue());
}
@Override
public void visit(WasmFloat32Constant expression) {
writer.writeByte(0x13);
writer.writeLEB(Float.floatToRawIntBits(expression.getValue()));
}
@Override
public void visit(WasmFloat64Constant expression) {
writer.writeByte(0x12);
writer.writeLEB(Double.doubleToRawLongBits(expression.getValue()));
}
@Override
public void visit(WasmGetLocal expression) {
writer.writeByte(0x14);
writer.writeLEB(expression.getLocal().getIndex());
}
@Override
public void visit(WasmSetLocal expression) {
expression.getValue().acceptVisitor(this);
writer.writeByte(0x15);
writer.writeLEB(expression.getLocal().getIndex());
}
@Override
public void visit(WasmIntBinary expression) {
expression.getFirst().acceptVisitor(this);
expression.getSecond().acceptVisitor(this);
switch (expression.getType()) {
case INT32:
switch (expression.getOperation()) {
case ADD:
writer.writeByte(0x40);
break;
case SUB:
writer.writeByte(0x41);
break;
case MUL:
writer.writeByte(0x42);
break;
case DIV_SIGNED:
writer.writeByte(0x43);
break;
case DIV_UNSIGNED:
writer.writeByte(0x44);
break;
case REM_SIGNED:
writer.writeByte(0x45);
break;
case REM_UNSIGNED:
writer.writeByte(0x46);
break;
case AND:
writer.writeByte(0x47);
break;
case OR:
writer.writeByte(0x48);
break;
case XOR:
writer.writeByte(0x49);
break;
case SHL:
writer.writeByte(0x4A);
break;
case SHR_UNSIGNED:
writer.writeByte(0x4B);
break;
case SHR_SIGNED:
writer.writeByte(0x4C);
break;
case ROTR:
writer.writeByte(0xB6);
break;
case ROTL:
writer.writeByte(0xB7);
break;
case EQ:
writer.writeByte(0x4D);
break;
case NE:
writer.writeByte(0x4E);
break;
case LT_SIGNED:
writer.writeByte(0x4F);
break;
case LE_SIGNED:
writer.writeByte(0x50);
break;
case LT_UNSIGNED:
writer.writeByte(0x51);
break;
case LE_UNSIGNED:
writer.writeByte(0x52);
break;
case GT_SIGNED:
writer.writeByte(0x53);
break;
case GE_SIGNED:
writer.writeByte(0x54);
break;
case GT_UNSIGNED:
writer.writeByte(0x55);
break;
case GE_UNSIGNED:
writer.writeByte(0x56);
break;
}
break;
case INT64:
switch (expression.getOperation()) {
case ADD:
writer.writeByte(0x5B);
break;
case SUB:
writer.writeByte(0x5C);
break;
case MUL:
writer.writeByte(0x5D);
break;
case DIV_SIGNED:
writer.writeByte(0x5E);
break;
case DIV_UNSIGNED:
writer.writeByte(0x5F);
break;
case REM_SIGNED:
writer.writeByte(0x60);
break;
case REM_UNSIGNED:
writer.writeByte(0x61);
break;
case AND:
writer.writeByte(0x62);
break;
case OR:
writer.writeByte(0x63);
break;
case XOR:
writer.writeByte(0x64);
break;
case SHL:
writer.writeByte(0x65);
break;
case SHR_UNSIGNED:
writer.writeByte(0x66);
break;
case SHR_SIGNED:
writer.writeByte(0x67);
break;
case ROTR:
writer.writeByte(0xB8);
break;
case ROTL:
writer.writeByte(0xB9);
break;
case EQ:
writer.writeByte(0x68);
break;
case NE:
writer.writeByte(0x69);
break;
case LT_SIGNED:
writer.writeByte(0x6A);
break;
case LE_SIGNED:
writer.writeByte(0x6B);
break;
case LT_UNSIGNED:
writer.writeByte(0x6C);
break;
case LE_UNSIGNED:
writer.writeByte(0x6D);
break;
case GT_SIGNED:
writer.writeByte(0x6E);
break;
case GE_SIGNED:
writer.writeByte(0x6F);
break;
case GT_UNSIGNED:
writer.writeByte(0x70);
break;
case GE_UNSIGNED:
writer.writeByte(0x71);
break;
}
break;
}
}
@Override
public void visit(WasmFloatBinary expression) {
expression.getFirst().acceptVisitor(this);
expression.getSecond().acceptVisitor(this);
switch (expression.getType()) {
case FLOAT32:
switch (expression.getOperation()) {
case ADD:
writer.writeByte(0x75);
break;
case SUB:
writer.writeByte(0x76);
break;
case MUL:
writer.writeByte(0x77);
break;
case DIV:
writer.writeByte(0x78);
break;
case MIN:
writer.writeByte(0x79);
break;
case MAX:
writer.writeByte(0x7A);
break;
case EQ:
writer.writeByte(0x83);
break;
case NE:
writer.writeByte(0x84);
break;
case LT:
writer.writeByte(0x85);
break;
case LE:
writer.writeByte(0x86);
break;
case GT:
writer.writeByte(0x87);
break;
case GE:
writer.writeByte(0x88);
break;
}
break;
case FLOAT64:
switch (expression.getOperation()) {
case ADD:
writer.writeByte(0x89);
break;
case SUB:
writer.writeByte(0x8A);
break;
case MUL:
writer.writeByte(0x8B);
break;
case DIV:
writer.writeByte(0x8C);
break;
case MIN:
writer.writeByte(0x8D);
break;
case MAX:
writer.writeByte(0x8E);
break;
case EQ:
writer.writeByte(0x97);
break;
case NE:
writer.writeByte(0x98);
break;
case LT:
writer.writeByte(0x99);
break;
case LE:
writer.writeByte(0x9A);
break;
case GT:
writer.writeByte(0x9B);
break;
case GE:
writer.writeByte(0x9C);
break;
}
break;
}
}
@Override
public void visit(WasmIntUnary expression) {
expression.getOperand().acceptVisitor(this);
switch (expression.getType()) {
case INT32:
switch (expression.getOperation()) {
case CLZ:
writer.writeByte(0x57);
break;
case CTZ:
writer.writeByte(0x58);
break;
case POPCNT:
writer.writeByte(0x59);
break;
}
break;
case INT64:
switch (expression.getOperation()) {
case CLZ:
writer.writeByte(0x72);
break;
case CTZ:
writer.writeByte(0x73);
break;
case POPCNT:
writer.writeByte(0x74);
break;
}
break;
}
}
@Override
public void visit(WasmFloatUnary expression) {
expression.getOperand().acceptVisitor(this);
switch (expression.getType()) {
case FLOAT32:
switch (expression.getOperation()) {
case ABS:
writer.writeByte(0x7B);
break;
case NEG:
writer.writeByte(0x7C);
break;
case COPYSIGN:
writer.writeByte(0x7D);
break;
case CEIL:
writer.writeByte(0x7E);
break;
case FLOOR:
writer.writeByte(0x7F);
break;
case TRUNC:
writer.writeByte(0x80);
break;
case NEAREST:
writer.writeByte(0x81);
break;
case SQRT:
writer.writeByte(0x82);
break;
}
break;
case FLOAT64:
switch (expression.getOperation()) {
case ABS:
writer.writeByte(0x8F);
break;
case NEG:
writer.writeByte(0x90);
break;
case COPYSIGN:
writer.writeByte(0x91);
break;
case CEIL:
writer.writeByte(0x92);
break;
case FLOOR:
writer.writeByte(0x93);
break;
case TRUNC:
writer.writeByte(0x94);
break;
case NEAREST:
writer.writeByte(0x95);
break;
case SQRT:
writer.writeByte(0x96);
break;
}
break;
}
}
@Override
public void visit(WasmConversion expression) {
expression.getOperand().acceptVisitor(this);
switch (expression.getSourceType()) {
case INT32:
switch (expression.getTargetType()) {
case INT32:
break;
case INT64:
writer.writeByte(expression.isSigned() ? 0xA6 : 0xA7);
break;
case FLOAT32:
writer.writeByte(expression.isSigned() ? 0xA8 : 0xA9);
break;
case FLOAT64:
writer.writeByte(expression.isSigned() ? 0xAE : 0xAF);
break;
}
break;
case INT64:
switch (expression.getTargetType()) {
case INT32:
writer.writeByte(0xA1);
break;
case INT64:
break;
case FLOAT32:
writer.writeByte(expression.isSigned() ? 0xAA : 0xAB);
break;
case FLOAT64:
writer.writeByte(expression.isSigned() ? 0xB0 : 0xB1);
break;
}
break;
case FLOAT32:
switch (expression.getTargetType()) {
case INT32:
writer.writeByte(expression.isSigned() ? 0x9D : 0x9F);
break;
case INT64:
writer.writeByte(expression.isSigned() ? 0xA2 : 0xA4);
break;
case FLOAT32:
break;
case FLOAT64:
writer.writeByte(0xB2);
break;
}
break;
case FLOAT64:
switch (expression.getTargetType()) {
case INT32:
writer.writeByte(expression.isSigned() ? 0x9E : 0xA0);
break;
case INT64:
writer.writeByte(expression.isSigned() ? 0xA3 : 0xA5);
break;
case FLOAT32:
writer.writeByte(0xAC);
break;
case FLOAT64:
break;
}
break;
}
}
@Override
public void visit(WasmCall expression) {
for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
writer.writeByte(!expression.isImported() ? 0x16 : 0x18);
writer.writeLEB(expression.getArguments().size());
writer.writeLEB(!expression.isImported()
? functionIndexes.get(expression.getFunctionName())
: importedIndexes.get(expression.getFunctionName()));
}
@Override
public void visit(WasmIndirectCall expression) {
expression.getSelector().acceptVisitor(this);
for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
writer.writeByte(0x17);
writer.writeLEB(expression.getArguments().size());
WasmType[] signatureTypes = new WasmType[expression.getParameterTypes().size() + 1];
signatureTypes[0] = expression.getReturnType();
for (int i = 0; i < expression.getParameterTypes().size(); ++i) {
signatureTypes[i + 1] = expression.getParameterTypes().get(i);
}
writer.writeLEB(signatureIndexes.get(new WasmSignature(signatureTypes)));
}
@Override
public void visit(WasmDrop expression) {
expression.getOperand().acceptVisitor(this);
}
@Override
public void visit(WasmLoadInt32 expression) {
expression.getIndex().acceptVisitor(this);
switch (expression.getConvertFrom()) {
case INT8:
writer.writeByte(0x20);
break;
case UINT8:
writer.writeByte(0x21);
break;
case INT16:
writer.writeByte(0x22);
break;
case UINT16:
writer.writeByte(0x23);
break;
case INT32:
writer.writeByte(0x2A);
break;
}
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmLoadInt64 expression) {
expression.getIndex().acceptVisitor(this);
switch (expression.getConvertFrom()) {
case INT8:
writer.writeByte(0x24);
break;
case UINT8:
writer.writeByte(0x25);
break;
case INT16:
writer.writeByte(0x26);
break;
case UINT16:
writer.writeByte(0x27);
break;
case INT32:
writer.writeByte(0x28);
break;
case UINT32:
writer.writeByte(0x29);
break;
case INT64:
writer.writeByte(0x2B);
break;
}
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmLoadFloat32 expression) {
expression.getIndex().acceptVisitor(this);
writer.writeByte(0x2C);
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmLoadFloat64 expression) {
expression.getIndex().acceptVisitor(this);
writer.writeByte(0x2D);
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmStoreInt32 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
switch (expression.getConvertTo()) {
case INT8:
case UINT8:
writer.writeByte(0x2E);
break;
case INT16:
case UINT16:
writer.writeByte(0x2F);
break;
case INT32:
writer.writeByte(0x33);
break;
}
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmStoreInt64 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
switch (expression.getConvertTo()) {
case INT8:
case UINT8:
writer.writeByte(0x30);
break;
case INT16:
case UINT16:
writer.writeByte(0x31);
break;
case INT32:
case UINT32:
writer.writeByte(0x32);
break;
case INT64:
writer.writeByte(0x34);
break;
}
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmStoreFloat32 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
writer.writeByte(0x35);
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
@Override
public void visit(WasmStoreFloat64 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
writer.writeByte(0x36);
writer.writeByte(getAlignment(expression.getAlignment()));
writer.writeByte(0);
}
private int getAlignment(int alignment) {
return 31 - Integer.numberOfLeadingZeros(Math.min(1, alignment));
}
private void writeLabel(WasmBlock target) {
int blockDepth = blockDepths.get(target);
writer.writeLEB(depth - blockDepth);
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2016 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.render;
import java.util.Arrays;
import org.teavm.backend.wasm.model.WasmType;
public class WasmBinaryWriter {
private byte[] data = new byte[1024];
private int pointer;
public void writeByte(int v) {
alloc(1);
data[pointer++] = (byte) v;
}
public void writeType(WasmType type) {
switch (type) {
case INT32:
writeByte(1);
break;
case INT64:
writeByte(2);
break;
case FLOAT32:
writeByte(3);
break;
case FLOAT64:
writeByte(4);
break;
}
}
public int getPosition() {
return pointer;
}
public void writeBytes(byte[] bytes) {
alloc(bytes.length);
System.arraycopy(bytes, 0, data, pointer, bytes.length);
pointer += bytes.length;
}
public void writeAsciiString(String str) {
writeLEB(str.length());
byte[] bytes = new byte[str.length()];
for (int i = 0; i < str.length(); ++i) {
bytes[i] = (byte) str.charAt(i);
}
writeBytes(bytes);
}
public void writeInt32(int v) {
alloc(4);
for (int i = 0; i < 4; ++i) {
data[pointer++] = (byte) (v & 0xFF);
v >>>= 8;
}
}
public void writeLEB(int v) {
alloc(5);
while (true) {
int digit = v & 0x7F;
int next = v >>> 7;
boolean last = next == 0;
if (!last) {
digit |= 0xFFFFFF80;
}
data[pointer++] = (byte) digit;
if (last) {
break;
}
v = next;
}
}
public void writeSignedLEB(int v) {
alloc(5);
boolean negative = v < 0;
while (true) {
int digit = (!negative ? v : (v << 25 >> 25)) & 0x7F;
int next = v >>> 7;
boolean last = next == 0;
if (!last) {
digit |= 0xFFFFFF80;
}
data[pointer++] = (byte) digit;
if (last) {
break;
}
v = next;
}
}
public void writeLEB(long v) {
alloc(10);
while (true) {
int digit = (int) (v & 0x7F);
long next = v >>> 7;
boolean last = next == 0;
if (!last) {
digit |= 0xFFFFFF80;
}
data[pointer++] = (byte) digit;
if (last) {
break;
}
v = next;
}
}
public void writeSignedLEB(long v) {
alloc(10);
boolean negative = v < 0;
while (true) {
int digit = (int) ((!negative ? v : (v << 25 >> 25)) & 0x7F);
long next = v >>> 7;
boolean last = next == 0;
if (!last) {
digit |= 0xFFFFFF80;
}
data[pointer++] = (byte) digit;
if (last) {
break;
}
v = next;
}
}
private void alloc(int size) {
if (data.length - pointer < size) {
int newLength = data.length * 2;
if (newLength < pointer + size) {
newLength = (pointer + size) * 2;
}
data = Arrays.copyOf(data, newLength);
}
}
public byte[] getData() {
return Arrays.copyOf(data, pointer);
}
}

View File

@ -20,7 +20,6 @@ import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmMemorySegment;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression;
public class WasmRenderer {
@ -143,23 +142,14 @@ public class WasmRenderer {
}
private void renderSignature(WasmFunction function) {
WasmSignature signature = signatureFromFunction(function);
WasmSignature signature = WasmSignature.fromFunction(function);
visitor.append(" ").open().append("type $type" + visitor.getSignatureIndex(signature)).close();
}
private WasmSignature signatureFromFunction(WasmFunction function) {
WasmType[] types = new WasmType[function.getParameters().size() + 1];
types[0] = function.getResult();
for (int i = 0; i < function.getParameters().size(); ++i) {
types[i + 1] = function.getParameters().get(i);
}
return new WasmSignature(types);
}
private void renderTypes(WasmModule module) {
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(visitor);
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(visitor::getSignatureIndex);
for (WasmFunction function : module.getFunctions().values()) {
visitor.getSignatureIndex(signatureFromFunction(function));
visitor.getSignatureIndex(WasmSignature.fromFunction(function));
for (WasmExpression part : function.getBody()) {
part.acceptVisitor(signatureCollector);
}

View File

@ -16,12 +16,13 @@
package org.teavm.backend.wasm.render;
import java.util.Arrays;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmType;
class WasmSignature {
final class WasmSignature {
WasmType[] types;
public WasmSignature(WasmType[] types) {
WasmSignature(WasmType[] types) {
this.types = types;
}
@ -41,4 +42,13 @@ class WasmSignature {
public int hashCode() {
return Arrays.hashCode(types);
}
public static WasmSignature fromFunction(WasmFunction function) {
WasmType[] types = new WasmType[function.getParameters().size() + 1];
types[0] = function.getResult();
for (int i = 0; i < function.getParameters().size(); ++i) {
types[i + 1] = function.getParameters().get(i);
}
return new WasmSignature(types);
}
}

View File

@ -15,15 +15,16 @@
*/
package org.teavm.backend.wasm.render;
import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
class WasmSignatureCollector extends WasmDefaultExpressionVisitor {
WasmRenderingVisitor renderingVisitor;
private Consumer<WasmSignature> consumer;
public WasmSignatureCollector(WasmRenderingVisitor renderingVisitor) {
this.renderingVisitor = renderingVisitor;
public WasmSignatureCollector(Consumer<WasmSignature> consumer) {
this.consumer = consumer;
}
@Override
@ -34,6 +35,6 @@ class WasmSignatureCollector extends WasmDefaultExpressionVisitor {
types[i + 1] = expression.getParameterTypes().get(i);
}
renderingVisitor.getSignatureIndex(new WasmSignature(types));
consumer.accept(new WasmSignature(types));
}
}