diff options
Diffstat (limited to 'src/Execution/MemInst.php')
| -rw-r--r-- | src/Execution/MemInst.php | 436 |
1 files changed, 304 insertions, 132 deletions
diff --git a/src/Execution/MemInst.php b/src/Execution/MemInst.php index 53414d7..21dd353 100644 --- a/src/Execution/MemInst.php +++ b/src/Execution/MemInst.php @@ -4,31 +4,165 @@ declare(strict_types=1); namespace Nsfisis\Waddiwasi\Execution; +use FFI; +use FFI\CData; use Nsfisis\Waddiwasi\Structure\Types\MemType; use function assert; -use function chr; use function count; -use function ord; -use function strlen; final class MemInst { - private string $data; - private const PAGE_SIZE = 64 * 1024; + private CData $dataU8; + private CData $dataS8; + + private CData $dataU16_0; + private CData $dataU16_1; + private CData $dataS16_0; + private CData $dataS16_1; + + private CData $dataU32_0; + private CData $dataU32_1; + private CData $dataU32_2; + private CData $dataU32_3; + private CData $dataS32_0; + private CData $dataS32_1; + private CData $dataS32_2; + private CData $dataS32_3; + + private CData $dataS64_0; + private CData $dataS64_1; + private CData $dataS64_2; + private CData $dataS64_3; + private CData $dataS64_4; + private CData $dataS64_5; + private CData $dataS64_6; + private CData $dataS64_7; + + private CData $dataF32_0; + private CData $dataF32_1; + private CData $dataF32_2; + private CData $dataF32_3; + + private CData $dataF64_0; + private CData $dataF64_1; + private CData $dataF64_2; + private CData $dataF64_3; + private CData $dataF64_4; + private CData $dataF64_5; + private CData $dataF64_6; + private CData $dataF64_7; + + private readonly int $dataSize; + + private readonly FFI $ffi; + public function __construct( public readonly MemType $type, ) { + $this->ffi = FFI::cdef(); + $minSize = $type->limits->min; // @todo hack $minSize *= 8; - $this->data = str_repeat("\0", $minSize * self::PAGE_SIZE); + $this->dataSize = $minSize * self::PAGE_SIZE; + + // @phpstan-ignore-next-line + $this->dataU8 = $this->ffi->new("uint8_t[$this->dataSize+8]"); + // @phpstan-ignore-next-line + $this->dataS8 = $this->ffi->cast("int8_t[$this->dataSize]", $this->dataU8); + + // @phpstan-ignore-next-line + $castInt = fn ($n, $signed, $offset) => $this->ffi->cast( + sprintf("%sint%d_t[$this->dataSize/%d]", $signed ? "" : "u", $n, $n / 8), + // @phpstan-ignore-next-line + $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), + ); + + // @phpstan-ignore-next-line + $castFloat = fn ($n, $offset) => $this->ffi->cast( + sprintf("%s[$this->dataSize/%d]", $n === 32 ? "float" : "double", $n / 8), + // @phpstan-ignore-next-line + $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), + ); + + // @phpstan-ignore-next-line + $this->dataU16_0 = $castInt(16, false, 0); + // @phpstan-ignore-next-line + $this->dataU16_1 = $castInt(16, false, 1); + // @phpstan-ignore-next-line + $this->dataS16_0 = $castInt(16, true, 0); + // @phpstan-ignore-next-line + $this->dataS16_1 = $castInt(16, true, 1); + + // @phpstan-ignore-next-line + $this->dataU32_0 = $castInt(32, false, 0); + // @phpstan-ignore-next-line + $this->dataU32_1 = $castInt(32, false, 1); + // @phpstan-ignore-next-line + $this->dataU32_2 = $castInt(32, false, 2); + // @phpstan-ignore-next-line + $this->dataU32_3 = $castInt(32, false, 3); + // @phpstan-ignore-next-line + $this->dataS32_0 = $castInt(32, true, 0); + // @phpstan-ignore-next-line + $this->dataS32_1 = $castInt(32, true, 1); + // @phpstan-ignore-next-line + $this->dataS32_2 = $castInt(32, true, 2); + // @phpstan-ignore-next-line + $this->dataS32_3 = $castInt(32, true, 3); + + // @phpstan-ignore-next-line + $this->dataS64_0 = $castInt(64, true, 0); + // @phpstan-ignore-next-line + $this->dataS64_1 = $castInt(64, true, 1); + // @phpstan-ignore-next-line + $this->dataS64_2 = $castInt(64, true, 2); + // @phpstan-ignore-next-line + $this->dataS64_3 = $castInt(64, true, 3); + // @phpstan-ignore-next-line + $this->dataS64_4 = $castInt(64, true, 4); + // @phpstan-ignore-next-line + $this->dataS64_5 = $castInt(64, true, 5); + // @phpstan-ignore-next-line + $this->dataS64_6 = $castInt(64, true, 6); + // @phpstan-ignore-next-line + $this->dataS64_7 = $castInt(64, true, 7); + + // @phpstan-ignore-next-line + $this->dataF32_0 = $castFloat(32, 0); + // @phpstan-ignore-next-line + $this->dataF32_1 = $castFloat(32, 1); + // @phpstan-ignore-next-line + $this->dataF32_2 = $castFloat(32, 2); + // @phpstan-ignore-next-line + $this->dataF32_3 = $castFloat(32, 3); + + // @phpstan-ignore-next-line + $this->dataF64_0 = $castFloat(64, 0); + // @phpstan-ignore-next-line + $this->dataF64_1 = $castFloat(64, 1); + // @phpstan-ignore-next-line + $this->dataF64_2 = $castFloat(64, 2); + // @phpstan-ignore-next-line + $this->dataF64_3 = $castFloat(64, 3); + // @phpstan-ignore-next-line + $this->dataF64_4 = $castFloat(64, 4); + // @phpstan-ignore-next-line + $this->dataF64_5 = $castFloat(64, 5); + // @phpstan-ignore-next-line + $this->dataF64_6 = $castFloat(64, 6); + // @phpstan-ignore-next-line + $this->dataF64_7 = $castFloat(64, 7); + + // @phpstan-ignore-next-line + FFI::memset($this->dataU8, 0, $this->dataSize); } public function size(): int { - return strlen($this->data); + return $this->dataSize; } /** @@ -51,13 +185,12 @@ final class MemInst */ public function loadI32_s8(int $ptr): ?int { - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return null; } - $result = unpack('c', $this->data, $ptr); - assert($result !== false); - $c = $result[1]; - assert(-0x80 <= $c && $c <= 0x7F); + // @phpstan-ignore-next-line + $c = $this->dataS8[$ptr]; + assert(-0x80 <= $c && $c <= 0x7F, "$c"); return $c; } @@ -66,13 +199,12 @@ final class MemInst */ public function loadI32_u8(int $ptr): ?int { - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return null; } - $result = unpack('C', $this->data, $ptr); - assert($result !== false); - $c = $result[1]; - assert(0x00 <= $c && $c <= 0xFF); + // @phpstan-ignore-next-line + $c = $this->dataU8[$ptr]; + assert(0 <= $c && $c <= 0xFF, "$c"); return $c; } @@ -81,13 +213,13 @@ final class MemInst */ public function loadI32_s16(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 2); - if ($buf === null) { + if ($this->size() <= $ptr + 2) { return null; } - $result = unpack('s', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataS16($ptr)[$ptr >> 1]; + assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); + return $c; } /** @@ -95,13 +227,13 @@ final class MemInst */ public function loadI32_u16(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 2); - if ($buf === null) { + if ($this->size() <= $ptr + 2) { return null; } - $result = unpack('S', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataU16($ptr)[$ptr >> 1]; + assert(0 <= $c && $c <= 0xFFFF, "$c"); + return $c; } /** @@ -109,13 +241,13 @@ final class MemInst */ public function loadI32_s32(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 4); - if ($buf === null) { + if ($this->size() <= $ptr + 4) { return null; } - $result = unpack('l', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataS32($ptr)[$ptr >> 2]; + assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); + return $c; } /** @@ -123,13 +255,12 @@ final class MemInst */ public function loadI64_s8(int $ptr): ?int { - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return null; } - $result = unpack('c', $this->data, $ptr); - assert($result !== false); - $c = $result[1]; - assert(-0x80 <= $c && $c <= 0x7F); + // @phpstan-ignore-next-line + $c = $this->dataS8[$ptr]; + assert(-0x80 <= $c && $c <= 0x7F, "$c"); return $c; } @@ -138,13 +269,12 @@ final class MemInst */ public function loadI64_u8(int $ptr): ?int { - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return null; } - $result = unpack('C', $this->data, $ptr); - assert($result !== false); - $c = $result[1]; - assert(0x00 <= $c && $c <= 0xFF); + // @phpstan-ignore-next-line + $c = $this->dataU8[$ptr]; + assert(0 <= $c && $c <= 0xFF, "$c"); return $c; } @@ -153,13 +283,13 @@ final class MemInst */ public function loadI64_s16(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 2); - if ($buf === null) { + if ($this->size() <= $ptr + 2) { return null; } - $result = unpack('s', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataS16($ptr)[$ptr >> 1]; + assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); + return $c; } /** @@ -167,13 +297,13 @@ final class MemInst */ public function loadI64_u16(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 2); - if ($buf === null) { + if ($this->size() <= $ptr + 2) { return null; } - $result = unpack('S', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataU16($ptr)[$ptr >> 1]; + assert(0 <= $c && $c <= 0xFFFF, "$c"); + return $c; } /** @@ -181,13 +311,13 @@ final class MemInst */ public function loadI64_s32(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 4); - if ($buf === null) { + if ($this->size() <= $ptr + 4) { return null; } - $result = unpack('l', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataS32($ptr)[$ptr >> 2]; + assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); + return $c; } /** @@ -195,13 +325,13 @@ final class MemInst */ public function loadI64_u32(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 4); - if ($buf === null) { + if ($this->size() <= $ptr + 4) { return null; } - $result = unpack('L', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataU32($ptr)[$ptr >> 2]; + assert(0 <= $c && $c <= 0xFFFFFFFF, "$c"); + return $c; } /** @@ -209,13 +339,13 @@ final class MemInst */ public function loadI64_s64(int $ptr): ?int { - $buf = $this->sliceNBytes($ptr, 8); - if ($buf === null) { + if ($this->size() <= $ptr + 8) { return null; } - $result = unpack('q', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + $c = $this->dataS64($ptr)[$ptr >> 3]; + assert(-0x8000000000000000 <= $c && $c <= 0x7FFFFFFFFFFFFFFF, "$c"); + return $c; } /** @@ -223,13 +353,11 @@ final class MemInst */ public function loadF32(int $ptr): ?float { - $buf = $this->sliceNBytes($ptr, 4); - if ($buf === null) { + if ($this->size() <= $ptr + 4) { return null; } - $result = unpack('f', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + return $this->dataF32($ptr)[$ptr >> 2]; } /** @@ -237,13 +365,11 @@ final class MemInst */ public function loadF64(int $ptr): ?float { - $buf = $this->sliceNBytes($ptr, 8); - if ($buf === null) { + if ($this->size() <= $ptr + 8) { return null; } - $result = unpack('d', $buf); - assert($result !== false); - return $result[1]; + // @phpstan-ignore-next-line + return $this->dataF64($ptr)[$ptr >> 3]; } /** @@ -251,14 +377,11 @@ final class MemInst */ public function loadByte(int $ptr): ?int { - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return null; } - $result = unpack('C', $this->data, $ptr); - assert($result !== false); - $c = $result[1]; - assert(0x00 <= $c && $c <= 0xFF); - return $c; + // @phpstan-ignore-next-line + return $this->dataU8[$ptr]; } /** @@ -266,11 +389,11 @@ final class MemInst */ public function storeByte(int $ptr, int $c): bool { - assert(0x00 <= $c && $c <= 0xFF); - if ($this->size() < $ptr) { + if ($this->size() <= $ptr + 1) { return false; } - $this->data[$ptr] = chr($c); + // @phpstan-ignore-next-line + $this->dataU8[$ptr] = $c; return true; } @@ -280,13 +403,11 @@ final class MemInst */ public function storeI32_s8(int $ptr, int $c): bool { - if ($this->size() < $ptr + 1) { + if ($this->size() <= $ptr + 1) { return false; } - $buf = pack('s', $c); - for ($i = 0; $i < 1; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS8[$ptr] = $c; return true; } @@ -296,13 +417,11 @@ final class MemInst */ public function storeI32_s16(int $ptr, int $c): bool { - if ($this->size() < $ptr + 2) { + if ($this->size() <= $ptr + 2) { return false; } - $buf = pack('s', $c); - for ($i = 0; $i < 2; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS16($ptr)[$ptr >> 1] = $c; return true; } @@ -312,13 +431,11 @@ final class MemInst */ public function storeI32_s32(int $ptr, int $c): bool { - if ($this->size() < $ptr + 4) { + if ($this->size() <= $ptr + 4) { return false; } - $buf = pack('l', $c); - for ($i = 0; $i < 4; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS32($ptr)[$ptr >> 2] = $c; return true; } @@ -328,7 +445,12 @@ final class MemInst */ public function storeI64_s8(int $ptr, int $c): bool { - return $this->storeByte($ptr, $c); + if ($this->size() <= $ptr + 1) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS8[$ptr] = $c; + return true; } /** @@ -337,13 +459,11 @@ final class MemInst */ public function storeI64_s16(int $ptr, int $c): bool { - if ($this->size() < $ptr + 2) { + if ($this->size() <= $ptr + 2) { return false; } - $buf = pack('s', $c); - for ($i = 0; $i < 2; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS16($ptr)[$ptr >> 1] = $c; return true; } @@ -353,13 +473,11 @@ final class MemInst */ public function storeI64_s32(int $ptr, int $c): bool { - if ($this->size() < $ptr + 4) { + if ($this->size() <= $ptr + 4) { return false; } - $buf = pack('l', $c); - for ($i = 0; $i < 4; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS32($ptr)[$ptr >> 2] = $c; return true; } @@ -369,13 +487,11 @@ final class MemInst */ public function storeI64_s64(int $ptr, int $c): bool { - if ($this->size() < $ptr + 8) { + if ($this->size() <= $ptr + 8) { return false; } - $buf = pack('q', $c); - for ($i = 0; $i < 8; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataS64($ptr)[$ptr >> 3] = $c; return true; } @@ -385,13 +501,11 @@ final class MemInst */ public function storeF32(int $ptr, float $c): bool { - if ($this->size() < $ptr + 4) { + if ($this->size() <= $ptr + 4) { return false; } - $buf = pack('f', $c); - for ($i = 0; $i < 4; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataF32($ptr)[$ptr >> 2] = $c; return true; } @@ -401,21 +515,79 @@ final class MemInst */ public function storeF64(int $ptr, float $c): bool { - if ($this->size() < $ptr + 8) { + if ($this->size() <= $ptr + 8) { return false; } - $buf = pack('d', $c); - for ($i = 0; $i < 8; $i++) { - $this->storeByte($ptr + $i, ord($buf[$i])); - } + // @phpstan-ignore-next-line + $this->dataF64($ptr)[$ptr >> 3] = $c; return true; } - private function sliceNBytes(int $ptr, int $len): ?string + private function dataU16(int $ptr): CData { - if ($this->size() < $ptr + $len) { - return null; - } - return substr($this->data, $ptr, $len); + return ($ptr & 1) !== 0 ? $this->dataU16_1 : $this->dataU16_0; + } + + private function dataS16(int $ptr): CData + { + return ($ptr & 1) !== 0 ? $this->dataS16_1 : $this->dataS16_0; + } + + private function dataU32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataU32_0, + 1 => $this->dataU32_1, + 2 => $this->dataU32_2, + 3 => $this->dataU32_3, + }; + } + + private function dataS32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataS32_0, + 1 => $this->dataS32_1, + 2 => $this->dataS32_2, + 3 => $this->dataS32_3, + }; + } + + private function dataS64(int $ptr): CData + { + return match ($ptr & 7) { + 0 => $this->dataS64_0, + 1 => $this->dataS64_1, + 2 => $this->dataS64_2, + 3 => $this->dataS64_3, + 4 => $this->dataS64_4, + 5 => $this->dataS64_5, + 6 => $this->dataS64_6, + 7 => $this->dataS64_7, + }; + } + + private function dataF32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataF32_0, + 1 => $this->dataF32_1, + 2 => $this->dataF32_2, + 3 => $this->dataF32_3, + }; + } + + private function dataF64(int $ptr): CData + { + return match ($ptr & 7) { + 0 => $this->dataF64_0, + 1 => $this->dataF64_1, + 2 => $this->dataF64_2, + 3 => $this->dataF64_3, + 4 => $this->dataF64_4, + 5 => $this->dataF64_5, + 6 => $this->dataF64_6, + 7 => $this->dataF64_7, + }; } } |
