aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Execution/MemInst.php
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-03-14 23:01:27 +0900
committernsfisis <nsfisis@gmail.com>2024-03-14 23:38:42 +0900
commita820647b099a266e4a578dbef67e50107b162f80 (patch)
treebe83b1b552cd4dbf5a287536109d969998d3a8ec /src/Execution/MemInst.php
parent77e4429b9bc8ef9d1d2791c6665c551895691645 (diff)
downloadphp-waddiwasi-a820647b099a266e4a578dbef67e50107b162f80.tar.gz
php-waddiwasi-a820647b099a266e4a578dbef67e50107b162f80.tar.zst
php-waddiwasi-a820647b099a266e4a578dbef67e50107b162f80.zip
perf: optimize memory allocation by FFI
Diffstat (limited to 'src/Execution/MemInst.php')
-rw-r--r--src/Execution/MemInst.php436
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,
+ };
}
}