All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.teavm.backend.wasm.WasmRuntime Maven / Gradle / Ivy

There is a newer version: 0.2.8
Show newest version
/*
 *  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;

import static org.teavm.interop.Memory.free;
import static org.teavm.interop.Memory.malloc;
import static org.teavm.interop.wasi.Wasi.printBuffer;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
import org.teavm.runtime.RuntimeObject;

@StaticInit
@Unmanaged
public final class WasmRuntime {
    private WasmRuntime() {
    }

    public static int compare(int a, int b) {
        return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
    }

    public static int compare(long a, long b) {
        return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
    }

    public static int compare(float a, float b) {
        return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
    }

    public static int compare(double a, double b) {
        return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
    }

    public static float remainder(float a, float b) {
        return a - (float) (int) (a / b) * b;
    }

    public static double remainder(double a, double b) {
        return a - (double) (long) (a / b) * b;
    }

    private static native boolean lt(int a, int b);

    private static native boolean gt(int a, int b);

    private static native boolean lt(long a, long b);

    private static native boolean gt(long a, long b);

    private static native boolean lt(float a, float b);

    private static native boolean gt(float a, float b);

    private static native boolean lt(double a, double b);

    private static native boolean gt(double a, double b);

    public 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);
    }

    public static int align(int value, int alignment) {
        if (value == 0) {
            return value;
        }
        value = ((value - 1) / alignment + 1) * alignment;
        return value;
    }

    @Import(name = "print", module = "spectest")
    public static native void print(int a);

    @Unmanaged
    public static void wasiPrintInt(int i) {
        if (i == 0) {
            wasiPrintString("0");
            return;
        }
        if (i == -2147483648) {
            wasiPrintString("-2147483648");
            return;
        }
        if (i < 0) {
            wasiPrintString("-");
            i = -i;
        }
        int bufferLength = 10;
        Address buffer = malloc(bufferLength, 1);
        int j = 0;
        while (i > 0 && j < bufferLength) {
            buffer.add(bufferLength - j - 1).putByte((byte) (48 + (i % 10)));
            i /= 10;
            ++j;
        }
        printBuffer(2, buffer.add(bufferLength - j), j);
        free(buffer, bufferLength, 1);
    }

    @Unmanaged
    public static void wasiPrintString(String s) {
        int length = s.length();
        Address buffer = malloc(length, 1);
        for (int i = 0; i < length; ++i) {
            buffer.add(i).putByte((byte) s.charAt(i));
        }
        printBuffer(2, buffer, length);
        free(buffer, length, 1);
    }

    @Unmanaged
    public static void wasiPrintOutOfMemory() {
       wasiPrintString("TeaVM: out of memory!");
    }

    public static void fillZero(Address address, int count) {
        fill(address, (byte) 0, count);
    }

    public static void fill(Address address, byte value, int count) {
        int value4 = (value & 0xFF << 24) | (value & 0xFF << 16) | (value & 0xFF << 8) | (value & 0xFF);
        int start = address.toInt();

        int alignedStart = start >>> 2 << 2;
        address = Address.fromInt(alignedStart);
        switch (start - alignedStart) {
            case 0:
                address.putInt(value4);
                break;
            case 1:
                address.add(1).putByte(value);
                address.add(2).putByte(value);
                address.add(3).putByte(value);
                break;
            case 2:
                address.add(2).putByte(value);
                address.add(3).putByte(value);
                break;
            case 3:
                address.add(3).putByte(value);
                break;
        }

        int end = start + count;
        int alignedEnd = end >>> 2 << 2;
        address = Address.fromInt(alignedEnd);
        switch (end - alignedEnd) {
            case 0:
                break;
            case 1:
                address.putByte(value);
                break;
            case 2:
                address.putByte(value);
                address.add(1).putByte(value);
                break;
            case 3:
                address.putByte(value);
                address.add(1).putByte(value);
                address.add(2).putByte(value);
                break;
        }

        for (address = Address.fromInt(alignedStart + 4); address.toInt() < alignedEnd; address = address.add(4)) {
            address.putInt(value4);
        }
    }

    public static void moveMemoryBlock(Address source, Address target, int count) {
        if (count < 8) {
            slowMemoryMove(source, target, count);
            return;
        }
        int diff = source.toInt() - target.toInt();
        if (diff == 0) {
            return;
        }
        if ((diff & 3) != 0) {
            slowMemoryMove(source, target, count);
            return;
        }

        Address alignedSourceStart = Address.fromInt(source.toInt() >>> 2 << 2);
        Address alignedTargetStart = Address.fromInt(target.toInt() >>> 2 << 2);

        Address alignedSourceEnd = Address.fromInt((source.toInt() + count) >>> 2 << 2);
        Address alignedTargetEnd = Address.fromInt((target.toInt() + count) >>> 2 << 2);

        if (source.toInt() > target.toInt()) {
            switch (source.toInt() - alignedSourceStart.toInt()) {
                case 0:
                    alignedTargetStart.putInt(alignedSourceStart.getInt());
                    break;
                case 1:
                    alignedTargetStart.add(1).putByte(alignedSourceStart.add(1).getByte());
                    alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort());
                    break;
                case 2:
                    alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort());
                    break;
                case 3:
                    alignedTargetStart.add(3).putByte(alignedSourceStart.add(3).getByte());
                    break;
            }

            alignedSourceStart = alignedSourceStart.add(4);
            alignedTargetStart = alignedTargetStart.add(4);

            while (alignedSourceStart.toInt() < alignedSourceEnd.toInt()) {
                alignedTargetStart.putInt(alignedSourceStart.getInt());
                alignedSourceStart = alignedSourceStart.add(4);
                alignedTargetStart = alignedTargetStart.add(4);
            }

            switch (source.toInt() + count - alignedSourceEnd.toInt()) {
                case 0:
                    break;
                case 1:
                    alignedTargetEnd.putByte(alignedSourceEnd.getByte());
                    break;
                case 2:
                    alignedTargetEnd.putShort(alignedSourceEnd.getShort());
                    break;
                case 3:
                    alignedTargetEnd.putShort(alignedSourceEnd.getShort());
                    alignedTargetEnd.add(2).putByte(alignedSourceEnd.add(2).getByte());
                    break;
            }
        } else {
            switch (source.toInt() + count - alignedSourceEnd.toInt()) {
                case 0:
                    break;
                case 1:
                    alignedTargetEnd.putByte(alignedSourceEnd.getByte());
                    break;
                case 2:
                    alignedTargetEnd.putShort(alignedSourceEnd.getShort());
                    break;
                case 3:
                    alignedTargetEnd.add(2).putByte(alignedSourceEnd.add(2).getByte());
                    alignedTargetEnd.putShort(alignedSourceEnd.getShort());
                    break;
            }

            while (alignedSourceEnd.toInt() > alignedSourceStart.toInt()) {
                alignedSourceEnd = alignedSourceEnd.add(-4);
                alignedTargetEnd = alignedTargetEnd.add(-4);
                alignedTargetEnd.putInt(alignedSourceEnd.getInt());
            }

            switch (source.toInt() - alignedSourceStart.toInt()) {
                case 1:
                    alignedTargetStart.add(-2).putShort(alignedSourceStart.add(-2).getShort());
                    alignedTargetStart.add(-3).putByte(alignedSourceStart.add(-3).getByte());
                    break;
                case 2:
                    alignedTargetStart.add(-2).putShort(alignedSourceStart.add(-2).getShort());
                    break;
                case 3:
                    alignedTargetStart.add(-1).putByte(alignedSourceStart.add(-1).getByte());
                    break;
            }
        }
    }

    private static void slowMemoryMove(Address source, Address target, int count) {
        if (source.toInt() > target.toInt()) {
            while (count-- > 0) {
                target.putByte(source.getByte());
                target = target.add(1);
                source = source.add(1);
            }
        } else {
            source = source.add(count);
            target = target.add(count);
            while (count-- > 0) {
                target = target.add(-1);
                source = source.add(-1);
                target.putByte(source.getByte());
            }
        }
    }

    public static Address allocStack(int size) {
        Address stack = WasmHeap.stack;
        Address result = stack.add(4);
        stack = result.add((size << 2) + 4);
        stack.putInt(size);
        WasmHeap.stack = stack;
        return result;
    }

    public static Address getStackTop() {
        return WasmHeap.stack != WasmHeap.stackAddress ? WasmHeap.stack : null;
    }

    public static Address getNextStackFrame(Address stackFrame) {
        int size = stackFrame.getInt() + 2;
        Address result = stackFrame.add(-size * 4);
        if (result == WasmHeap.stackAddress) {
            result = null;
        }
        return result;
    }

    public static int getStackRootCount(Address stackFrame) {
        return stackFrame.getInt();
    }

    public static Address getStackRootPointer(Address stackFrame) {
        int size = stackFrame.getInt();
        return stackFrame.add(-size * 4);
    }

    private static Address getExceptionHandlerPtr(Address stackFrame) {
        int size = stackFrame.getInt();
        return stackFrame.add(-size * 4 - 4);
    }

    public static int getCallSiteId(Address stackFrame) {
        return getExceptionHandlerPtr(stackFrame).getInt();
    }

    public static void setExceptionHandlerId(Address stackFrame, int id) {
        getExceptionHandlerPtr(stackFrame).putInt(id);
    }

    private static int hashCode(RuntimeString string) {
        int hashCode = 0;
        int length = string.characters.length;
        Address chars = Address.ofData(string.characters);
        for (int i = 0; i < length; ++i) {
            hashCode = 31 * hashCode + chars.getChar();
            chars = chars.add(2);
        }
        return hashCode;
    }

    private static boolean equals(RuntimeString first, RuntimeString second) {
        if (first.characters.length != second.characters.length) {
            return false;
        }

        Address firstChars = Address.ofData(first.characters);
        Address secondChars = Address.ofData(second.characters);
        int length = first.characters.length;
        for (int i = 0; i < length; ++i) {
            if (firstChars.getChar() != secondChars.getChar()) {
                return false;
            }
            firstChars = firstChars.add(2);
            secondChars = secondChars.add(2);
        }
        return true;
    }

    public static String[] resourceMapKeys(Address map) {
        String[] result = new String[resourceMapSize(map)];
        fillResourceMapKeys(map, result);
        return result;
    }

    private static int resourceMapSize(Address map) {
        int result = 0;
        int sz = map.getInt();
        Address data = contentStart(map);
        for (int i = 0; i < sz; ++i) {
            if (data.getAddress() != null) {
                result++;
            }
            data = data.add(Address.sizeOf() * 2);
        }

        return result;
    }

    private static void fillResourceMapKeys(Address map, String[] target) {
        int sz = map.getInt();
        Address data = contentStart(map);
        Address targetData = Address.ofData(target);
        for (int i = 0; i < sz; ++i) {
            Address entry = data.getAddress();
            if (entry != null) {
                targetData.putAddress(entry);
                targetData = targetData.add(Address.sizeOf());
            }
            data = data.add(Address.sizeOf());
        }
    }

    private static Address contentStart(Address resource) {
        return resource.add(Address.sizeOf());
    }

    public static Address lookupResource(Address map, String string) {
        RuntimeString runtimeString = Address.ofObject(string).toStructure();
        int hashCode = hashCode(runtimeString);
        int sz = map.getInt();
        Address content = contentStart(map);
        for (int i = 0; i < sz; ++i) {
            int index = (hashCode + i) % sz;
            if (index < 0) {
                index += sz;
            }
            Address entry = content.add(index * Address.sizeOf() * 2);
            Address key = entry.getAddress();
            if (key == null) {
                return null;
            }
            if (equals(key.toStructure(), runtimeString)) {
                return entry;
            }
        }
        return null;
    }

    static class RuntimeString extends RuntimeObject {
        char[] characters;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy