Add compressed binary data to store RTTI

This commit is contained in:
Alexey Andreev 2016-08-15 14:45:01 +03:00
parent 421eca8a49
commit 38638bc560
15 changed files with 1457 additions and 100 deletions

View File

@ -20,15 +20,6 @@ import org.teavm.interop.Structure;
public class RuntimeClass extends Structure {
public static int INITIALIZED = 1;
public static int SIZE_OFFSET = 0;
public static int FLAGS_OFFSET = 4;
public static int LOWER_TAG_OFFSET = 8;
public static int UPPER_TAG_OFFSET = 12;
public static int EXCLUDED_RANGE_COUNT_OFFSET = 16;
public static int EXCLUDED_RANGE_ADDRESS_OFFSET = 20;
public static int CANARY_OFFSET = 24;
public static int VIRTUAL_TABLE_OFFSET = 28;
public static int BOOLEAN_CLASS = -1;
public static int BYTE_CLASS = -2;
public static int SHORT_CLASS = -3;
@ -40,17 +31,15 @@ public class RuntimeClass extends Structure {
public int size;
public int flags;
public int lowerTag;
public int upperTag;
public int excludedRangeCount;
public int excludedRangesAddress;
public int tag;
public int canary;
public static int computeCanary(int size, int lowerTag, int upperTag) {
return size ^ (lowerTag << 8) ^ (lowerTag >>> 24) ^ (upperTag << 24) ^ (lowerTag >>> 8);
public static int computeCanary(int size, int tag) {
return size ^ (tag << 8) ^ (tag >>> 24) ^ (0xAAAAAAAA);
}
public int computeCanary() {
return computeCanary(size, lowerTag, upperTag);
return computeCanary(size, tag);
}
public static native RuntimeClass getArrayClass();

View File

@ -15,9 +15,12 @@
*/
package org.teavm.wasm;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
public final class WasmRuntime {
private static Address offset;
private WasmRuntime() {
}
@ -61,6 +64,131 @@ public final class WasmRuntime {
private static native boolean gt(double a, double b);
public static void decodeData(Address data, Address metadata) {
offset = metadata;
int count = decodeLeb();
while (count-- > 0) {
data = decodeDataRec(data);
}
}
private static Address decodeDataRec(Address data) {
Address metadata = offset;
int type = metadata.getByte();
metadata = metadata.add(1);
switch (type) {
case 0:
data = data.add(1);
break;
case 1: {
data = align(data, 2);
byte a = data.getByte();
byte b = data.add(1).getByte();
int value = (a << 8) | b;
data.putShort((short) value);
data = data.add(2);
break;
}
case 2:
case 4:
case 6: {
data = align(data, 4);
int value = getInt(data);
data.putInt(value);
data = data.add(4);
break;
}
case 3:
case 5: {
data = align(data, 8);
data.putLong(getLong(data));
data = data.add(8);
break;
}
case 7: {
offset = metadata;
int size = decodeLeb();
metadata = offset;
while (size-- > 0) {
offset = metadata;
data = decodeDataRec(data);
}
metadata = offset;
break;
}
case 8: {
int alignment = metadata.getByte();
offset = metadata.add(1);
int size = decodeLeb();
if (alignment > 0) {
data = align(data, alignment);
}
while (size-- > 0) {
data = decodeDataRec(data);
}
metadata = offset;
break;
}
case 9: {
Address start = metadata.add(-1);
offset = metadata;
int back = decodeLeb();
metadata = offset;
offset = start.add(-back);
data = decodeDataRec(data);
break;
}
}
offset = metadata;
return data;
}
private static int getInt(Address data) {
byte a = data.getByte();
byte b = data.add(1).getByte();
byte c = data.add(2).getByte();
byte d = data.add(3).getByte();
return (a << 24) | (b << 16) | (c << 8) | d;
}
private static long getLong(Address data) {
long a = data.getByte();
long b = data.add(1).getByte();
long c = data.add(2).getByte();
long d = data.add(3).getByte();
long e = data.add(4).getByte();
long f = data.add(5).getByte();
long g = data.add(6).getByte();
long h = data.add(7).getByte();
return (a << 56) | (b << 48) | (c << 40) | (d << 32) | (e << 24) | (f << 16) | (g << 8) | h;
}
private static Address align(Address address, int alignment) {
int value = address.toInt();
if (value == 0) {
return address;
}
value = ((value - 1) / alignment + 1) * alignment;
return Address.fromInt(value);
}
private static int decodeLeb() {
Address index = offset;
int result = 0;
int shift = 0;
int value;
do {
value = index.getByte();
index = index.add(1);
result |= (value & 0x7F) << shift;
} while ((value & 0x80) != 0);
offset = index;
return result;
}
@Import(name = "print", module = "spectest")
public static native void print(int a);
}

View File

@ -56,6 +56,7 @@ import org.teavm.vm.TeaVMEntryPoint;
import org.teavm.vm.TeaVMTarget;
import org.teavm.vm.TeaVMTargetController;
import org.teavm.vm.spi.TeaVMHostExtension;
import org.teavm.wasm.binary.BinaryWriter;
import org.teavm.wasm.generate.WasmClassGenerator;
import org.teavm.wasm.generate.WasmGenerationContext;
import org.teavm.wasm.generate.WasmGenerator;
@ -65,6 +66,7 @@ import org.teavm.wasm.intrinsics.WasmRuntimeClassIntrinsic;
import org.teavm.wasm.intrinsics.WasmRuntimeIntrinsic;
import org.teavm.wasm.intrinsics.WasmStructureIntrinsic;
import org.teavm.wasm.model.WasmFunction;
import org.teavm.wasm.model.WasmMemorySegment;
import org.teavm.wasm.model.WasmModule;
import org.teavm.wasm.model.WasmType;
import org.teavm.wasm.model.expression.WasmBlock;
@ -110,6 +112,9 @@ public class WasmTarget implements TeaVMTarget {
dependencyChecker.linkMethod(method, null).use();
}
dependencyChecker.linkMethod(new MethodReference(WasmRuntime.class, "decodeData", Address.class,
Address.class, void.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
RuntimeClass.class, Address.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateArray",
@ -120,15 +125,14 @@ public class WasmTarget implements TeaVMTarget {
@Override
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
int address = 256;
WasmModule module = new WasmModule();
WasmFunction initFunction = new WasmFunction("__start__");
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
TagRegistry tagRegistry = new TagRegistry(classes);
BinaryWriter binaryWriter = new BinaryWriter(256);
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, tagRegistry,
initFunction.getBody());
classGenerator.setAddress(address);
binaryWriter);
for (String className : classes.getClassNames()) {
classGenerator.addClass(className);
if (controller.wasCancelled()) {
@ -136,7 +140,6 @@ public class WasmTarget implements TeaVMTarget {
}
}
classGenerator.addArrayClass();
address = classGenerator.getAddress();
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
new HashSet<>());
@ -181,7 +184,24 @@ public class WasmTarget implements TeaVMTarget {
}
}
renderAllocatorInit(module, address);
WasmMemorySegment dataSegment = new WasmMemorySegment();
dataSegment.setData(binaryWriter.getData());
dataSegment.setOffset(256);
module.getSegments().add(dataSegment);
WasmMemorySegment metadataSegment = new WasmMemorySegment();
metadataSegment.setData(binaryWriter.getMetadata());
metadataSegment.setOffset(binaryWriter.getAddress());
module.getSegments().add(metadataSegment);
MethodReference initData = new MethodReference(WasmRuntime.class, "decodeData", Address.class,
Address.class, void.class);
WasmCall initDataCall = new WasmCall(WasmMangling.mangleMethod(initData));
initDataCall.getArguments().add(new WasmInt32Constant(256));
initDataCall.getArguments().add(new WasmInt32Constant(binaryWriter.getAddress()));
initFunction.getBody().add(initDataCall);
renderAllocatorInit(module, binaryWriter.getAddress());
renderClinit(classes, classGenerator, module);
if (controller.wasCancelled()) {
return;

View File

@ -0,0 +1,21 @@
/*
* 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.wasm.binary;
public enum AddressMode {
ADDRESS_32,
ADDRESS_64
}

View File

@ -0,0 +1,184 @@
/*
* 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.wasm.binary;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BinaryWriter {
private int address;
private List<DataValue> values = new ArrayList<>();
public BinaryWriter(int start) {
this.address = start;
}
public int append(DataValue value) {
int result = align(address, getAlignment(value.getType()));
values.add(value);
address = offset(value.getType(), result);
return result;
}
public int getAddress() {
return address;
}
private int offset(DataType type, int index) {
if (type == DataPrimitives.BYTE) {
return index + 1;
} else if (type == DataPrimitives.SHORT) {
return align(index, 2) + 2;
} else if (type == DataPrimitives.INT) {
return align(index, 4) + 4;
} else if (type == DataPrimitives.LONG) {
return align(index, 8) + 8;
} else if (type == DataPrimitives.FLOAT) {
return align(index, 4) + 4;
} else if (type == DataPrimitives.DOUBLE) {
return align(index, 8) + 8;
} else if (type == DataPrimitives.ADDRESS) {
return align(index, 4) + 4;
} else if (type instanceof DataArray) {
DataArray array = (DataArray) type;
int next = offset(array.getComponentType(), index);
return index + (next - index) * array.getSize();
} else if (type instanceof DataStructure) {
DataStructure structure = (DataStructure) type;
if (structure.getAlignment() > 0) {
index = align(index, structure.getAlignment());
}
DataType[] components = structure.getComponents();
for (DataType component : components) {
index = offset(component, index);
}
return index;
} else {
return index;
}
}
private static int align(int address, int alignment) {
if (address == 0) {
return 0;
}
return ((address - 1) / alignment + 1) * alignment;
}
private int getAlignment(DataType type) {
if (type == DataPrimitives.BYTE) {
return 1;
} else if (type == DataPrimitives.SHORT) {
return 2;
} else if (type == DataPrimitives.INT) {
return 4;
} else if (type == DataPrimitives.LONG) {
return 8;
} else if (type == DataPrimitives.FLOAT) {
return 4;
} else if (type == DataPrimitives.DOUBLE) {
return 8;
} else if (type == DataPrimitives.ADDRESS) {
return 4;
} else if (type instanceof DataArray) {
return getAlignment(((DataArray) type).getComponentType());
} else if (type instanceof DataStructure) {
DataStructure structure = (DataStructure) type;
return Math.max(structure.getAlignment(), getAlignment(structure.getComponents()[0]));
} else {
return 1;
}
}
public byte[] getData() {
byte[] result = new byte[address];
int offset = 0;
for (DataValue value : values) {
offset = writeData(result, offset, value);
}
return Arrays.copyOf(result, offset);
}
private int writeData(byte[] result, int offset, DataValue value) {
DataType type = value.getType();
if (type == DataPrimitives.BYTE) {
result[offset++] = value.getByte(0);
} else if (type == DataPrimitives.SHORT) {
offset = align(offset, 2);
short v = value.getShort(0);
result[offset++] = (byte) (v >> 8);
result[offset++] = (byte) v;
} else if (type == DataPrimitives.INT) {
offset = align(offset, 4);
int v = value.getInt(0);
result[offset++] = (byte) (v >> 24);
result[offset++] = (byte) (v >> 16);
result[offset++] = (byte) (v >> 8);
result[offset++] = (byte) v;
} else if (type == DataPrimitives.LONG) {
offset = align(offset, 8);
long v = value.getInt(0);
result[offset++] = (byte) (v >> 56);
result[offset++] = (byte) (v >> 48);
result[offset++] = (byte) (v >> 40);
result[offset++] = (byte) (v >> 32);
result[offset++] = (byte) (v >> 24);
result[offset++] = (byte) (v >> 16);
result[offset++] = (byte) (v >> 8);
result[offset++] = (byte) v;
} else if (type == DataPrimitives.FLOAT) {
offset = align(offset, 4);
int v = Float.floatToRawIntBits(value.getInt(0));
result[offset++] = (byte) (v >> 24);
result[offset++] = (byte) (v >> 16);
result[offset++] = (byte) (v >> 8);
result[offset++] = (byte) v;
} else if (type == DataPrimitives.DOUBLE) {
offset = align(offset, 8);
long v = Double.doubleToRawLongBits(value.getDouble(0));
result[offset++] = (byte) (v >> 56);
result[offset++] = (byte) (v >> 48);
result[offset++] = (byte) (v >> 40);
result[offset++] = (byte) (v >> 32);
result[offset++] = (byte) (v >> 24);
result[offset++] = (byte) (v >> 16);
result[offset++] = (byte) (v >> 8);
result[offset++] = (byte) v;
} else if (type instanceof DataArray) {
DataArray array = (DataArray) type;
for (int i = 0; i < array.getSize(); ++i) {
offset = writeData(result, offset, value.getValue(i));
}
} else if (type instanceof DataStructure) {
DataStructure structure = (DataStructure) type;
if (structure.getAlignment() > 0) {
offset = align(offset, structure.getAlignment());
}
int componentCount = structure.getComponents().length;
for (int i = 0; i < componentCount; ++i) {
offset = writeData(result, offset, value.getValue(i));
}
}
return offset;
}
public byte[] getMetadata() {
MetadataWriter writer = new MetadataWriter();
writer.write(values.stream().map(value -> value.getType()).toArray(DataType[]::new));
return writer.getData();
}
}

View File

@ -0,0 +1,429 @@
/*
* 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.wasm.binary;
public class DataArray extends DataType {
private DataType componentType;
private int size;
public DataArray(DataType componentType, int size) {
this.componentType = componentType;
this.size = size;
}
public DataType getComponentType() {
return componentType;
}
public int getSize() {
return size;
}
@Override
public DataValue createValue() {
if (componentType == DataPrimitives.BYTE) {
return new ByteArrayValue(this);
} else if (componentType == DataPrimitives.SHORT) {
return new ShortArrayValue(this);
} else if (componentType == DataPrimitives.INT) {
return new IntArrayValue(this);
} else if (componentType == DataPrimitives.LONG) {
return new LongArrayValue(this);
} else if (componentType == DataPrimitives.ADDRESS) {
return new AddressArrayValue(this);
} else if (componentType == DataPrimitives.FLOAT) {
return new FloatArrayValue(this);
} else if (componentType == DataPrimitives.DOUBLE) {
return new DoubleArrayValue(this);
}
return new ArrayValue(this);
}
static class ByteArrayValue extends DataValue {
byte[] data;
ByteArrayValue(DataArray type) {
super(type);
data = new byte[type.size];
}
@Override
public byte getByte(int index) {
return data[index];
}
@Override
public void setByte(int index, byte value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.BYTE) {
@Override
public byte getByte(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setByte(int index, byte value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class ShortArrayValue extends DataValue {
short[] data;
ShortArrayValue(DataArray type) {
super(type);
data = new short[type.size];
}
@Override
public short getShort(int index) {
return data[index];
}
@Override
public void setShort(int index, short value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.SHORT) {
@Override
public short getShort(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setShort(int index, short value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class IntArrayValue extends DataValue {
int[] data;
IntArrayValue(DataArray type) {
super(type);
data = new int[type.size];
}
@Override
public int getInt(int index) {
return data[index];
}
@Override
public void setInt(int index, int value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.INT) {
@Override
public int getInt(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setInt(int index, int value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class LongArrayValue extends DataValue {
long[] data;
LongArrayValue(DataArray type) {
super(type);
data = new long[type.size];
}
@Override
public long getLong(int index) {
return data[index];
}
@Override
public void setLong(int index, long value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.LONG) {
@Override
public long getLong(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setLong(int index, long value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class AddressArrayValue extends DataValue {
long[] data;
AddressArrayValue(DataArray type) {
super(type);
data = new long[type.size];
}
@Override
public long getAddress(int index) {
return data[index];
}
@Override
public void setAddress(int index, long value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.ADDRESS) {
@Override
public long getAddress(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setAddress(int index, long value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class FloatArrayValue extends DataValue {
float[] data;
FloatArrayValue(DataArray type) {
super(type);
data = new float[type.size];
}
@Override
public float getFloat(int index) {
return data[index];
}
@Override
public void setFloat(int index, float value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.FLOAT) {
@Override
public float getFloat(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setFloat(int index, float value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class DoubleArrayValue extends DataValue {
double[] data;
DoubleArrayValue(DataArray type) {
super(type);
data = new double[type.size];
}
@Override
public double getDouble(int index) {
return data[index];
}
@Override
public void setDouble(int index, double value) {
data[index] = value;
}
@Override
public DataValue getValue(int index) {
int outerIndex = index;
return new DataValue(DataPrimitives.DOUBLE) {
@Override
public double getDouble(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
return data[outerIndex];
}
@Override
public void setDouble(int index, double value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0");
}
data[outerIndex] = value;
}
};
}
}
static class ArrayValue extends DataValue {
DataValue[] data;
ArrayValue(DataArray type) {
super(type);
data = new DataValue[type.size];
for (int i = 0; i < data.length; ++i) {
data[i] = type.componentType.createValue();
}
}
@Override
public byte getByte(int index) {
return data[index].getByte(0);
}
@Override
public void setByte(int index, byte value) {
data[index].setByte(0, value);
}
@Override
public short getShort(int index) {
return data[index].getShort(0);
}
@Override
public void setShort(int index, short value) {
data[index].setShort(0, value);
}
@Override
public int getInt(int index) {
return data[index].getInt(0);
}
@Override
public void setInt(int index, int value) {
data[index].setInt(0, value);
}
@Override
public long getLong(int index) {
return data[index].getLong(0);
}
@Override
public void setLong(int index, long value) {
data[index].setLong(0, value);
}
@Override
public long getAddress(int index) {
return data[index].getAddress(0);
}
@Override
public void setAddress(int index, long value) {
data[index].setAddress(0, value);
}
@Override
public float getFloat(int index) {
return data[index].getFloat(0);
}
@Override
public void setFloat(int index, float value) {
data[index].setFloat(0, value);
}
@Override
public double getDouble(int index) {
return data[index].getDouble(0);
}
@Override
public void setDouble(int index, double value) {
data[index].setDouble(0, value);
}
@Override
public DataValue getValue(int index) {
return data[index];
}
}
}

View File

@ -0,0 +1,238 @@
/*
* 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.wasm.binary;
public final class DataPrimitives {
private DataPrimitives() {
}
public static final DataType BYTE = new DataType() {
@Override
public DataValue createValue() {
return new ByteDataValue();
}
};
public static final DataType SHORT = new DataType() {
@Override
public DataValue createValue() {
return new ShortDataValue();
}
};
public static final DataType INT = new DataType() {
@Override
public DataValue createValue() {
return new IntDataValue();
}
};
public static final DataType LONG = new DataType() {
@Override
public DataValue createValue() {
return new LongDataValue();
}
};
public static final DataType ADDRESS = new DataType() {
@Override
public DataValue createValue() {
return new AddressDataValue();
}
};
public static final DataType FLOAT = new DataType() {
@Override
public DataValue createValue() {
return new FloatDataValue();
}
};
public static final DataType DOUBLE = new DataType() {
@Override
public DataValue createValue() {
return new DoubleDataValue();
}
};
static class ByteDataValue extends DataValue {
byte value;
ByteDataValue() {
super(BYTE);
}
@Override
public byte getByte(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setByte(int index, byte value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class ShortDataValue extends DataValue {
short value;
ShortDataValue() {
super(SHORT);
}
@Override
public short getShort(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setShort(int index, short value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class IntDataValue extends DataValue {
int value;
IntDataValue() {
super(INT);
}
@Override
public int getInt(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setInt(int index, int value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class LongDataValue extends DataValue {
long value;
LongDataValue() {
super(LONG);
}
@Override
public long getLong(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setLong(int index, long value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class AddressDataValue extends DataValue {
long value;
AddressDataValue() {
super(ADDRESS);
}
@Override
public long getAddress(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setAddress(int index, long value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class FloatDataValue extends DataValue {
float value;
FloatDataValue() {
super(FLOAT);
}
@Override
public float getFloat(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setFloat(int index, float value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
static class DoubleDataValue extends DataValue {
double value;
DoubleDataValue() {
super(DOUBLE);
}
@Override
public double getDouble(int index) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
return value;
}
@Override
public void setDouble(int index, double value) {
if (index != 0) {
throw new IllegalArgumentException("Index should be 0 for primitive values");
}
this.value = value;
}
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.wasm.binary;
public class DataStructure extends DataType {
private byte alignment;
private DataType[] components;
public DataStructure(byte alignment, DataType... components) {
this.alignment = alignment;
this.components = components.clone();
}
public byte getAlignment() {
return alignment;
}
public DataType[] getComponents() {
return components.clone();
}
@Override
public DataValue createValue() {
return new StructureValue(this);
}
static class StructureValue extends DataValue {
private DataValue[] components;
StructureValue(DataStructure type) {
super(type);
components = new DataValue[type.components.length];
for (int i = 0; i < components.length; ++i) {
components[i] = type.components[i].createValue();
}
}
@Override
public void setByte(int index, byte value) {
components[index].setByte(0, value);
}
@Override
public void setShort(int index, short value) {
components[index].setShort(0, value);
}
@Override
public void setInt(int index, int value) {
components[index].setInt(0, value);
}
@Override
public void setLong(int index, long value) {
components[index].setLong(0, value);
}
@Override
public void setAddress(int index, long value) {
components[index].setAddress(0, value);
}
@Override
public void setFloat(int index, float value) {
components[index].setFloat(0, value);
}
@Override
public void setDouble(int index, double value) {
components[index].setDouble(0, value);
}
@Override
public DataValue getValue(int index) {
return components[index];
}
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.wasm.binary;
public abstract class DataType {
public abstract DataValue createValue();
}

View File

@ -0,0 +1,89 @@
/*
* 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.wasm.binary;
public abstract class DataValue {
private DataType type;
DataValue(DataType type) {
this.type = type;
}
public DataType getType() {
return type;
}
public byte getByte(int index) {
throw new UnsupportedOperationException();
}
public void setByte(int index, byte value) {
throw new UnsupportedOperationException();
}
public short getShort(int index) {
throw new UnsupportedOperationException();
}
public void setShort(int index, short value) {
throw new UnsupportedOperationException();
}
public int getInt(int index) {
throw new UnsupportedOperationException();
}
public void setInt(int index, int value) {
throw new UnsupportedOperationException();
}
public long getLong(int index) {
throw new UnsupportedOperationException();
}
public void setLong(int index, long value) {
throw new UnsupportedOperationException();
}
public long getAddress(int index) {
throw new UnsupportedOperationException();
}
public void setAddress(int index, long value) {
throw new UnsupportedOperationException();
}
public float getFloat(int index) {
throw new UnsupportedOperationException();
}
public void setFloat(int index, float value) {
throw new UnsupportedOperationException();
}
public double getDouble(int index) {
throw new UnsupportedOperationException();
}
public void setDouble(int index, double value) {
throw new UnsupportedOperationException();
}
public DataValue getValue(int index) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.wasm.binary;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class MetadataWriter {
private byte[] data = new byte[256];
private int offset;
private Map<DataStructure, Integer> structureUsages = new HashMap<>();
void write(DataType[] types) {
writeLeb(types.length);
for (DataType type : types) {
write(type);
}
}
private void write(DataType type) {
if (type == DataPrimitives.BYTE) {
writeByte(0);
} else if (type == DataPrimitives.SHORT) {
writeByte(1);
} else if (type == DataPrimitives.INT) {
writeByte(2);
} else if (type == DataPrimitives.LONG) {
writeByte(3);
} else if (type == DataPrimitives.FLOAT) {
writeByte(4);
} else if (type == DataPrimitives.DOUBLE) {
writeByte(5);
} else if (type == DataPrimitives.ADDRESS) {
writeByte(6);
} else if (type instanceof DataArray) {
DataArray array = (DataArray) type;
writeByte(7);
writeLeb(array.getSize());
write(array.getComponentType());
} else if (type instanceof DataStructure) {
DataStructure structure = (DataStructure) type;
Integer usage = structureUsages.get(structure);
if (usage == null) {
structureUsages.put(structure, offset);
writeByte(8);
writeByte(structure.getAlignment());
DataType[] components = structure.getComponents();
writeLeb(components.length);
for (int i = 0; i < components.length; ++i) {
write(components[i]);
}
} else {
int start = offset;
structureUsages.put(structure, offset);
writeByte(9);
writeLeb(start - usage);
}
}
}
private void writeLeb(int value) {
if (value >>> 7 == 0) {
writeByte(value);
} else if (value >>> 14 == 0) {
byte first = (byte) ((value & 0x7F) | 0x80);
byte second = (byte) (value >>> 7);
writeBytes(new byte[] { first, second });
} else if (value >>> 21 == 0) {
byte first = (byte) ((value & 0x7F) | 0x80);
byte second = (byte) (((value >>> 7) & 0x7F) | 0x80);
byte third = (byte) (value >>> 14);
writeBytes(new byte[] { first, second, third });
} else if (value >>> 28 == 0) {
byte first = (byte) ((value & 0x7F) | 0x80);
byte second = (byte) (((value >>> 7) & 0x7F) | 0x80);
byte third = (byte) (((value >>> 14) & 0x7F) | 0x80);
byte fourth = (byte) (value >>> 21);
writeBytes(new byte[] { first, second, third, fourth });
} else {
byte first = (byte) ((value & 0x7F) | 0x80);
byte second = (byte) (((value >>> 7) & 0x7F) | 0x80);
byte third = (byte) (((value >>> 14) & 0x7F) | 0x80);
byte fourth = (byte) (((value >>> 21) & 0x7F) | 0x80);
byte fifth = (byte) (value >>> 28);
writeBytes(new byte[] { first, second, third, fourth, fifth });
}
}
private void writeByte(int value) {
if (offset >= data.length) {
data = Arrays.copyOf(data, data.length * 2);
}
data[offset++] = (byte) value;
}
private void writeBytes(byte[] values) {
int count = values.length;
if (offset + count > data.length) {
data = Arrays.copyOf(data, data.length * 2);
}
System.arraycopy(values, 0, data, offset, count);
offset += count;
}
public byte[] getData() {
return Arrays.copyOf(data, offset);
}
}

View File

@ -18,8 +18,6 @@ package org.teavm.wasm.generate;
import com.carrotsearch.hppc.ObjectIntMap;
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@ -38,28 +36,35 @@ import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.runtime.RuntimeClass;
import org.teavm.wasm.model.expression.WasmExpression;
import org.teavm.wasm.model.expression.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype;
import org.teavm.wasm.model.expression.WasmStoreInt32;
import org.teavm.wasm.binary.BinaryWriter;
import org.teavm.wasm.binary.DataArray;
import org.teavm.wasm.binary.DataPrimitives;
import org.teavm.wasm.binary.DataStructure;
import org.teavm.wasm.binary.DataType;
import org.teavm.wasm.binary.DataValue;
public class WasmClassGenerator {
private ClassReaderSource classSource;
private int address;
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
private ClassBinaryData arrayClassData;
private List<WasmExpression> initializer;
private BinaryWriter binaryWriter;
private Map<MethodReference, Integer> functions = new HashMap<>();
private List<String> functionTable = new ArrayList<>();
private VirtualTableProvider vtableProvider;
private TagRegistry tagRegistry;
private DataStructure classStructure = new DataStructure(
(byte) 8,
DataPrimitives.INT, /* size */
DataPrimitives.INT, /* flags */
DataPrimitives.INT, /* tag */
DataPrimitives.INT /* canary */);
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider,
TagRegistry tagRegistry, List<WasmExpression> initializer) {
TagRegistry tagRegistry, BinaryWriter binaryWriter) {
this.classSource = classSource;
this.vtableProvider = vtableProvider;
this.tagRegistry = tagRegistry;
this.initializer = initializer;
this.binaryWriter = binaryWriter;
}
public void addClass(String className) {
@ -77,9 +82,7 @@ public class WasmClassGenerator {
return;
}
address = align(address, 8);
binaryData.start = address;
contributeToInitializer(binaryData);
binaryData.start = binaryWriter.append(createStructure(binaryData));
}
public void addArrayClass() {
@ -88,71 +91,48 @@ public class WasmClassGenerator {
}
arrayClassData = new ClassBinaryData();
arrayClassData.start = address;
address += RuntimeClass.VIRTUAL_TABLE_OFFSET;
}
public int getAddress() {
return address;
}
public void setAddress(int address) {
this.address = address;
arrayClassData.start = binaryWriter.append(classStructure.createValue());
}
public List<String> getFunctionTable() {
return functionTable;
}
private void contributeToInitializer(ClassBinaryData binaryData) {
contributeInt32Value(binaryData.start + RuntimeClass.SIZE_OFFSET, binaryData.size);
List<TagRegistry.Range> ranges = tagRegistry.getRanges(binaryData.name);
int lower = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
int upper = ranges.stream().mapToInt(range -> range.upper).max().orElse(0);
contributeInt32Value(binaryData.start + RuntimeClass.LOWER_TAG_OFFSET, lower);
contributeInt32Value(binaryData.start + RuntimeClass.UPPER_TAG_OFFSET, upper);
contributeInt32Value(binaryData.start + RuntimeClass.CANARY_OFFSET,
RuntimeClass.computeCanary(binaryData.size, lower, upper));
address = binaryData.start + RuntimeClass.VIRTUAL_TABLE_OFFSET;
private DataValue createStructure(ClassBinaryData binaryData) {
VirtualTable vtable = vtableProvider.lookup(binaryData.name);
if (vtable != null) {
for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) {
int methodIndex;
if (vtableEntry.getImplementor() == null) {
methodIndex = -1;
} else {
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
int result = functionTable.size();
functionTable.add(WasmMangling.mangleMethod(implementor));
return result;
});
}
int vtableSize = vtable != null ? vtable.getEntries().size() : 0;
contributeInt32Value(address, methodIndex);
address += 4;
}
DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
DataValue array = wrapper.getValue(1);
DataValue header = wrapper.getValue(0);
header.setInt(0, binaryData.size);
List<TagRegistry.Range> ranges = tagRegistry.getRanges(binaryData.name);
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
header.setInt(2, tag);
header.setInt(3, RuntimeClass.computeCanary(binaryData.size, tag));
if (vtable == null) {
return header;
}
contributeInt32Value(binaryData.start + RuntimeClass.EXCLUDED_RANGE_COUNT_OFFSET, ranges.size() - 1);
if (ranges.size() > 1) {
contributeInt32Value(binaryData.start + RuntimeClass.EXCLUDED_RANGE_ADDRESS_OFFSET, address);
Collections.sort(ranges, Comparator.comparingInt(range -> range.lower));
for (int i = 1; i < ranges.size(); ++i) {
contributeInt32Value(address, ranges.get(i - 1).upper);
contributeInt32Value(address + 4, ranges.get(i).lower);
address += 8;
int index = 0;
for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) {
int methodIndex;
if (vtableEntry.getImplementor() == null) {
methodIndex = -1;
} else {
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
int result = functionTable.size();
functionTable.add(WasmMangling.mangleMethod(implementor));
return result;
});
}
}
}
private void contributeInt32Value(int index, int value) {
initializer.add(new WasmStoreInt32(4, new WasmInt32Constant(index), new WasmInt32Constant(value),
WasmInt32Subtype.INT32));
array.setInt(index++, methodIndex);
}
return wrapper;
}
public int getClassPointer(String className) {
@ -197,9 +177,9 @@ public class WasmClassGenerator {
for (FieldReader field : cls.getFields()) {
int desiredAlignment = getDesiredAlignment(field.getType());
if (field.hasModifier(ElementModifier.STATIC)) {
int offset = align(address, desiredAlignment);
/*int offset = align(address, desiredAlignment);
data.fieldLayout.put(field.getName(), offset);
address = offset + desiredAlignment;
address = offset + desiredAlignment;*/
} else {
int offset = align(data.size, desiredAlignment);
data.fieldLayout.put(field.getName(), offset);

View File

@ -256,6 +256,18 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
expr.getSecondOperand().acceptVisitor(this);
WasmExpression second = result;
if (expr.getType() == OperationType.LONG) {
switch (expr.getOperation()) {
case LEFT_SHIFT:
case RIGHT_SHIFT:
case UNSIGNED_RIGHT_SHIFT:
second = new WasmConversion(WasmType.INT32, WasmType.INT64, false, second);
break;
default:
break;
}
}
switch (expr.getType()) {
case INT:
result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
@ -661,8 +673,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
getReferenceToClass(instance), new WasmInt32Constant(vtableEntry.getIndex() * 4
+ RuntimeClass.VIRTUAL_TABLE_OFFSET));
getReferenceToClass(instance), new WasmInt32Constant(vtableEntry.getIndex() * 4 + 16));
methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32);
WasmIndirectCall call = new WasmIndirectCall(methodIndex);
@ -833,7 +844,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmBlock block = new WasmBlock(false);
WasmLocal tagVar = function.getLocalVariables().get(getTemporaryInt32());
WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
getReferenceToClass(result), new WasmInt32Constant(RuntimeClass.LOWER_TAG_OFFSET));
getReferenceToClass(result), new WasmInt32Constant(8));
block.getBody().add(new WasmSetLocal(tagVar, new WasmLoadInt32(4, tagPtr, WasmInt32Subtype.INT32)));
WasmExpression lowerThanMinCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
@ -963,21 +974,56 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 0;
}
private boolean isBoolean(WasmExpression expression) {
if (expression instanceof WasmIntBinary) {
WasmIntBinary binary = (WasmIntBinary) expression;
switch (binary.getOperation()) {
case EQ:
case NE:
case LT_SIGNED:
case LT_UNSIGNED:
case LE_SIGNED:
case LE_UNSIGNED:
case GT_SIGNED:
case GT_UNSIGNED:
case GE_SIGNED:
case GE_UNSIGNED:
return true;
default:
return false;
}
} else if (expression instanceof WasmFloatBinary) {
WasmFloatBinary binary = (WasmFloatBinary) expression;
switch (binary.getOperation()) {
case EQ:
case NE:
case LT:
case LE:
case GT:
case GE:
return true;
default:
return false;
}
}
return false;
}
private WasmExpression forCondition(WasmExpression expression) {
if (expression instanceof WasmIntBinary) {
WasmIntBinary binary = (WasmIntBinary) expression;
switch (binary.getOperation()) {
case EQ:
if (isZero(binary.getFirst())) {
if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) {
return negate(binary.getSecond());
} else if (isZero(binary.getSecond())) {
} else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) {
return negate(binary.getFirst());
}
break;
case NE:
if (isZero(binary.getFirst())) {
if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) {
return binary.getSecond();
} else if (isZero(binary.getSecond())) {
} else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) {
return binary.getFirst();
}
break;

View File

@ -46,6 +46,6 @@ public class WasmMemorySegment {
}
public byte[] getData(int offset, int size) {
return Arrays.copyOfRange(data, offset, size);
return Arrays.copyOfRange(data, offset, offset + size);
}
}

View File

@ -80,16 +80,16 @@ public class WasmRenderer {
visitor.lf();
visitor.open().append("memory " + module.getMemorySize());
for (WasmMemorySegment segment : module.getSegments()) {
visitor.lf().open().append("segment " + segment.getLength());
visitor.lf().open().append("segment " + segment.getOffset());
visitor.indent();
for (int i = 0; i < segment.getLength(); i += 256) {
visitor.lf().append("\"");
byte[] part = segment.getData(i, Math.max(segment.getLength(), i + 256) - i);
byte[] part = segment.getData(i, Math.min(segment.getLength(), i + 256) - i);
StringBuilder sb = new StringBuilder();
for (int j = 0; j < part.length; ++j) {
int b = part[j] << 24 >>> 24;
if (b < ' ' || b > 126) {
sb.append("\\0x" + Character.forDigit(b >> 4, 16) + Character.forDigit(b & 0xF, 16));
sb.append("\\" + Character.forDigit(b >> 4, 16) + Character.forDigit(b & 0xF, 16));
} else if (b == '\\') {
sb.append("\\\\");
} else if (b == '"') {