aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-02-26 13:21:22 +0900
committernsfisis <nsfisis@gmail.com>2024-03-03 15:27:26 +0900
commit996259e6a1a04f91179d2b83cc19e3fbb371cd33 (patch)
tree14ccabf37cc909c8c61899ffb84429fdeb8dfce0 /src
parent8a789bffe72e93ae24ea39648018e5fecf94c7fa (diff)
downloadphp-waddiwasi-996259e6a1a04f91179d2b83cc19e3fbb371cd33.tar.gz
php-waddiwasi-996259e6a1a04f91179d2b83cc19e3fbb371cd33.tar.zst
php-waddiwasi-996259e6a1a04f91179d2b83cc19e3fbb371cd33.zip
feat: implement some of instructions
Diffstat (limited to 'src')
-rw-r--r--src/BinaryFormat/Decoder.php8
-rw-r--r--src/Debug/Debug.php113
-rw-r--r--src/Execution/Allocator.php25
-rw-r--r--src/Execution/ControlFlowResult.php20
-rw-r--r--src/Execution/ControlFlowResults/Br.php16
-rw-r--r--src/Execution/ControlFlowResults/Return_.php14
-rw-r--r--src/Execution/FuncInst.php4
-rw-r--r--src/Execution/FuncInsts/Host.php4
-rw-r--r--src/Execution/GlobalInst.php4
-rw-r--r--src/Execution/MemInst.php4
-rw-r--r--src/Execution/Runtime.php833
-rw-r--r--src/Execution/Stack.php166
-rw-r--r--src/Execution/StackEntries/Frame.php6
-rw-r--r--src/Execution/StackEntries/Label.php7
-rw-r--r--src/Execution/StackEntries/Value.php4
-rw-r--r--src/Execution/StackEntry.php8
-rw-r--r--src/Execution/TableInst.php4
-rw-r--r--src/Execution/TrapException.php11
-rw-r--r--src/Execution/Val.php49
-rw-r--r--src/Structure/Types/FuncType.php10
-rw-r--r--src/Structure/Types/ResultType.php13
-rw-r--r--src/Structure/Types/ValType.php2
-rw-r--r--src/Structure/Types/ValTypes/NumType.php6
-rw-r--r--src/Structure/Types/ValTypes/RefType.php6
-rw-r--r--src/Structure/Types/ValTypes/VecType.php6
25 files changed, 1078 insertions, 265 deletions
diff --git a/src/BinaryFormat/Decoder.php b/src/BinaryFormat/Decoder.php
index ae6e9f2..4922482 100644
--- a/src/BinaryFormat/Decoder.php
+++ b/src/BinaryFormat/Decoder.php
@@ -293,11 +293,11 @@ final class Decoder
if ($b !== 0x60) {
throw new InvalidBinaryFormatException("functype");
}
- $args = $this->decodeResultType();
- $returns = $this->decodeResultType();
+ $params = $this->decodeResultType();
+ $results = $this->decodeResultType();
return new FuncType(
- $args,
- $returns,
+ $params,
+ $results,
);
}
diff --git a/src/Debug/Debug.php b/src/Debug/Debug.php
new file mode 100644
index 0000000..d33aa8b
--- /dev/null
+++ b/src/Debug/Debug.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nsfisis\Waddiwasi\Debug;
+
+use Nsfisis\Waddiwasi\Structure\Modules\ImportDescs;
+use Nsfisis\Waddiwasi\Structure\Modules\Module;
+use Nsfisis\Waddiwasi\Structure\Types\FuncType;
+use Nsfisis\Waddiwasi\Structure\Types\GlobalType;
+use Nsfisis\Waddiwasi\Structure\Types\Limits;
+use Nsfisis\Waddiwasi\Structure\Types\MemType;
+use Nsfisis\Waddiwasi\Structure\Types\Mut;
+use Nsfisis\Waddiwasi\Structure\Types\NumType;
+use Nsfisis\Waddiwasi\Structure\Types\RefType;
+use Nsfisis\Waddiwasi\Structure\Types\ResultType;
+use Nsfisis\Waddiwasi\Structure\Types\TableType;
+use Nsfisis\Waddiwasi\Structure\Types\TypeIdx;
+use Nsfisis\Waddiwasi\Structure\Types\ValType;
+use Nsfisis\Waddiwasi\Structure\Types\ValTypes;
+
+final readonly class Debug
+{
+ public static function printImports(Module $module): void
+ {
+ foreach ($module->imports as $import) {
+ $mod = $import->module;
+ $name = $import->name;
+ $desc = $import->desc;
+ $type = match ($desc::class) {
+ ImportDescs\Func::class => 'func',
+ ImportDescs\Table::class => 'table',
+ ImportDescs\Mem::class => 'mem',
+ ImportDescs\Global_::class => 'global',
+ default => 'unknown',
+ };
+ $extraInfo = match ($desc::class) {
+ ImportDescs\Func::class => self::typeIdxToString($desc->func, $module),
+ ImportDescs\Table::class => self::tableTypeToString($desc->table),
+ ImportDescs\Mem::class => self::memTypeToString($desc->mem),
+ ImportDescs\Global_::class => self::globalTypeToString($desc->global),
+ default => 'unknown',
+ };
+ echo "$mod::$name: $type $extraInfo\n";
+ }
+ }
+
+ private static function typeIdxToString(TypeIdx $idx, Module $module): string
+ {
+ $type = $module->types[$idx->value];
+ return self::funcTypeToString($type);
+ }
+
+ private static function tableTypeToString(TableType $type): string
+ {
+ return self::valTypeToString(ValType::RefType($type->refType)) . ' ' . self::limitsToString($type->limits);
+ }
+
+ private static function memTypeToString(MemType $type): string
+ {
+ return self::limitsToString($type->limits);
+ }
+
+ private static function globalTypeToString(GlobalType $type): string
+ {
+ return self::mutToString($type->mut) . ' ' . self::valTypeToString($type->valType);
+ }
+
+ private static function funcTypeToString(FuncType $type): string
+ {
+ $params = self::resultTypeToString($type->params);
+ $results = self::resultTypeToString($type->results);
+ return "($params) -> ($results)";
+ }
+
+ private static function valTypeToString(ValType $type): string
+ {
+ return match ($type::class) {
+ ValTypes\NumType::class => match ($type->inner) {
+ NumType::I32 => 'i32',
+ NumType::I64 => 'i64',
+ NumType::F32 => 'f32',
+ NumType::F64 => 'f64',
+ },
+ ValTypes\VecType::class => 'v128',
+ ValTypes\RefType::class => match ($type->inner) {
+ RefType::FuncRef => 'funcref',
+ RefType::ExternRef => 'externref',
+ },
+ default => 'unknown',
+ };
+ }
+
+ private static function limitsToString(Limits $limit): string
+ {
+ $min = $limit->min;
+ $max = $limit->max;
+ return $max === null ? "[$min, inf)" : "[$min, $max]";
+ }
+
+ private static function mutToString(Mut $mut): string
+ {
+ return match ($mut) {
+ Mut::Const => 'const',
+ Mut::Var => 'var',
+ };
+ }
+
+ private static function resultTypeToString(ResultType $type): string
+ {
+ return implode(', ', array_map(self::valTypeToString(...), $type->types));
+ }
+}
diff --git a/src/Execution/Allocator.php b/src/Execution/Allocator.php
index ad62fc7..89e1f83 100644
--- a/src/Execution/Allocator.php
+++ b/src/Execution/Allocator.php
@@ -29,9 +29,11 @@ final readonly class Allocator
$m = new ModuleInst($module->types, [], [], [], [], [], [], []);
foreach ($externVals as $externVal) {
- if ($externVal instanceof ExternVals\Global_) {
- $m->globalAddrs[] = $externVal->addr;
- }
+ match ($externVal::class) {
+ ExternVals\Func::class => $m->funcAddrs[] = $externVal->addr,
+ ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr,
+ default => null,
+ };
}
foreach ($module->funcs as $func) {
$m->funcAddrs[] = $this->allocFunc($func, $m);
@@ -57,7 +59,7 @@ final readonly class Allocator
foreach ($externVals as $externVal) {
match ($externVal::class) {
- ExternVals\Func::class => $m->funcAddrs[] = $externVal->addr,
+ ExternVals\Func::class => null, // handled below.
ExternVals\Table::class => $m->tableAddrs[] = $externVal->addr,
ExternVals\Mem::class => $m->memAddrs[] = $externVal->addr,
ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr,
@@ -68,13 +70,14 @@ final readonly class Allocator
foreach ($preAllocatedFuncs as $funcAddr) {
$m->funcAddrs[] = $funcAddr;
$funcInst = $this->store->funcs[$funcAddr->value];
- assert($funcInst instanceof FuncInsts\Wasm);
- // @phpstan-ignore-next-line
- $this->store->funcs[$funcAddr->value] = FuncInst::Wasm(
- $funcInst->type,
- $m,
- $funcInst->code,
- );
+ if ($funcInst instanceof FuncInsts\Wasm) {
+ // @phpstan-ignore-next-line
+ $this->store->funcs[$funcAddr->value] = FuncInst::Wasm(
+ $funcInst->type,
+ $m,
+ $funcInst->code,
+ );
+ }
}
foreach ($module->tables as $table) {
$m->tableAddrs[] = $this->allocTable($table->type, Ref::RefNull($table->type->refType));
diff --git a/src/Execution/ControlFlowResult.php b/src/Execution/ControlFlowResult.php
new file mode 100644
index 0000000..1b995d9
--- /dev/null
+++ b/src/Execution/ControlFlowResult.php
@@ -0,0 +1,20 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nsfisis\Waddiwasi\Execution;
+
+use Nsfisis\Waddiwasi\Structure\Types\LabelIdx;
+
+abstract readonly class ControlFlowResult
+{
+ final public static function Br(LabelIdx $label): ControlFlowResults\Br
+ {
+ return new ControlFlowResults\Br($label);
+ }
+
+ final public static function Return(): ControlFlowResults\Return_
+ {
+ return new ControlFlowResults\Return_();
+ }
+}
diff --git a/src/Execution/ControlFlowResults/Br.php b/src/Execution/ControlFlowResults/Br.php
new file mode 100644
index 0000000..8ca7ff3
--- /dev/null
+++ b/src/Execution/ControlFlowResults/Br.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nsfisis\Waddiwasi\Execution\ControlFlowResults;
+
+use Nsfisis\Waddiwasi\Execution\ControlFlowResult;
+use Nsfisis\Waddiwasi\Structure\Types\LabelIdx;
+
+final readonly class Br extends ControlFlowResult
+{
+ protected function __construct(
+ public LabelIdx $label,
+ ) {
+ }
+}
diff --git a/src/Execution/ControlFlowResults/Return_.php b/src/Execution/ControlFlowResults/Return_.php
new file mode 100644
index 0000000..a9e0885
--- /dev/null
+++ b/src/Execution/ControlFlowResults/Return_.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nsfisis\Waddiwasi\Execution\ControlFlowResults;
+
+use Nsfisis\Waddiwasi\Execution\ControlFlowResult;
+
+final readonly class Return_ extends ControlFlowResult
+{
+ protected function __construct()
+ {
+ }
+}
diff --git a/src/Execution/FuncInst.php b/src/Execution/FuncInst.php
index c69eb80..901fa81 100644
--- a/src/Execution/FuncInst.php
+++ b/src/Execution/FuncInst.php
@@ -14,8 +14,8 @@ abstract readonly class FuncInst
return new FuncInsts\Wasm($type, $module, $code);
}
- final public static function Host(FuncType $type, callable $code): FuncInsts\Host
+ final public static function Host(FuncType $type, callable $callback): FuncInsts\Host
{
- return new FuncInsts\Host($type, $code);
+ return new FuncInsts\Host($type, $callback);
}
}
diff --git a/src/Execution/FuncInsts/Host.php b/src/Execution/FuncInsts/Host.php
index e3cba4a..67686b4 100644
--- a/src/Execution/FuncInsts/Host.php
+++ b/src/Execution/FuncInsts/Host.php
@@ -10,11 +10,11 @@ use Nsfisis\Waddiwasi\Structure\Types\FuncType;
final readonly class Host extends FuncInst
{
/**
- * @param callable $code
+ * @param callable $callback
*/
public function __construct(
public FuncType $type,
- public mixed $code,
+ public mixed $callback,
) {
}
}
diff --git a/src/Execution/GlobalInst.php b/src/Execution/GlobalInst.php
index 47bd9a0..5595b45 100644
--- a/src/Execution/GlobalInst.php
+++ b/src/Execution/GlobalInst.php
@@ -6,10 +6,10 @@ namespace Nsfisis\Waddiwasi\Execution;
use Nsfisis\Waddiwasi\Structure\Types\GlobalType;
-final readonly class GlobalInst
+final class GlobalInst
{
public function __construct(
- public GlobalType $type,
+ public readonly GlobalType $type,
public Val $value,
) {
}
diff --git a/src/Execution/MemInst.php b/src/Execution/MemInst.php
index 3999052..240e415 100644
--- a/src/Execution/MemInst.php
+++ b/src/Execution/MemInst.php
@@ -6,13 +6,13 @@ namespace Nsfisis\Waddiwasi\Execution;
use Nsfisis\Waddiwasi\Structure\Types\MemType;
-final readonly class MemInst
+final class MemInst
{
/**
* @param list<Byte> $data
*/
public function __construct(
- public MemType $type,
+ public readonly MemType $type,
public array $data,
) {
}
diff --git a/src/Execution/Runtime.php b/src/Execution/Runtime.php
index 030c46e..86cb74b 100644
--- a/src/Execution/Runtime.php
+++ b/src/Execution/Runtime.php
@@ -6,13 +6,21 @@ namespace Nsfisis\Waddiwasi\Execution;
use Nsfisis\Waddiwasi\Structure\Instructions\Instr;
use Nsfisis\Waddiwasi\Structure\Instructions\Instrs;
+use Nsfisis\Waddiwasi\Structure\Instructions\Instrs\Control\BlockType;
+use Nsfisis\Waddiwasi\Structure\Instructions\Instrs\Control\BlockTypes;
use Nsfisis\Waddiwasi\Structure\Modules\DataModes;
use Nsfisis\Waddiwasi\Structure\Modules\ElemModes;
use Nsfisis\Waddiwasi\Structure\Modules\Module;
use Nsfisis\Waddiwasi\Structure\Types\DataIdx;
use Nsfisis\Waddiwasi\Structure\Types\ElemIdx;
+use Nsfisis\Waddiwasi\Structure\Types\FuncType;
+use Nsfisis\Waddiwasi\Structure\Types\LabelIdx;
+use Nsfisis\Waddiwasi\Structure\Types\NumType;
+use Nsfisis\Waddiwasi\Structure\Types\ResultType;
+use Nsfisis\Waddiwasi\Structure\Types\TableIdx;
+use Nsfisis\Waddiwasi\Structure\Types\ValType;
+use Nsfisis\Waddiwasi\Structure\Types\ValTypes;
-// @todo split this class into multiple classes
final readonly class Runtime
{
private function __construct(
@@ -44,7 +52,7 @@ final readonly class Runtime
foreach ($module->globals as $global) {
$instrs = $global->init->instrs;
array_pop($instrs); // drop "end"
- $vals[] = $runtimeInit->evalInstrs($instrs);
+ $vals[] = $runtimeInit->evalInstrsForInit($instrs);
assert($stack->top() === $frameInit);
}
@@ -54,7 +62,7 @@ final readonly class Runtime
foreach ($elem->init as $expr) {
$instrs = $expr->instrs;
array_pop($instrs); // drop "end"
- $result = $runtimeInit->evalInstrs($instrs);
+ $result = $runtimeInit->evalInstrsForInit($instrs);
assert($result instanceof Vals\Ref);
$refs[] = $result->inner;
}
@@ -86,9 +94,9 @@ final readonly class Runtime
$instrs[] = Instr::I32Const($n);
$instrs[] = Instr::TableInit($elem->mode->table, new ElemIdx($i));
$instrs[] = Instr::ElemDrop(new ElemIdx($i));
- $runtime->execInstrs($instrs);
+ $runtime->execInstrsForInit($instrs);
} elseif ($elem->mode instanceof ElemModes\Declarative) {
- $runtime->execInstrs([Instr::ElemDrop(new ElemIdx($i))]);
+ $runtime->execInstrsForInit([Instr::ElemDrop(new ElemIdx($i))]);
}
}
foreach ($module->datas as $i => $data) {
@@ -101,12 +109,12 @@ final readonly class Runtime
$instrs[] = Instr::I32Const($n);
$instrs[] = Instr::MemoryInit(new DataIdx($i));
$instrs[] = Instr::DataDrop(new DataIdx($i));
- $runtime->execInstrs($instrs);
+ $runtime->execInstrsForInit($instrs);
}
}
if ($module->start !== null) {
- $runtime->execInstrs([Instr::Call($module->start->func)]);
+ $runtime->execInstrsForInit([Instr::Call($module->start->func)]);
}
assert($stack->top() === $frame);
@@ -115,447 +123,828 @@ final readonly class Runtime
return new self($store, $stack, $moduleInst);
}
- public function invoke(): void
+ public function getExport(string $name): ?ExternVal
{
+ foreach ($this->module->exports as $export) {
+ if ($name === $export->name) {
+ return $export->value;
+ }
+ }
+ return null;
+ }
+
+ public function getExportedMemory(string $name): ?MemInst
+ {
+ $export = $this->getExport($name);
+ if ($export instanceof ExternVals\Mem) {
+ return $this->store->mems[$export->addr->value];
+ }
+ return null;
+ }
+
+ /**
+ * @param list<Val> $vals
+ * @return list<Val>
+ */
+ public function invoke(string $name, array $vals): array
+ {
+ $export = $this->getExport($name);
+ assert($export instanceof ExternVals\Func);
+ $funcAddr = $export->addr;
+
+ $funcInst = $this->store->funcs[$funcAddr->value];
+ assert($funcInst instanceof FuncInsts\Wasm);
+ $paramTypes = $funcInst->type->params->types;
+ $resultTypes = $funcInst->type->results->types;
+ if (count($paramTypes) !== count($vals)) {
+ throw new \RuntimeException("invoke($name) invalid function arity: expected " . count($paramTypes) . ", got " . count($vals));
+ }
+ $f = StackEntry::Frame(0, [], new ModuleInst([], [], [], [], [], [], [], []));
+ $this->stack->push($f);
+ foreach ($vals as $val) {
+ $this->stack->pushValue($val);
+ }
+ $this->doInvokeFunc($funcAddr);
+ $results = [];
+ for ($i = 0; $i < count($resultTypes); $i++) {
+ $results[] = $this->stack->popValue();
+ }
+ $this->stack->pop();
+ return $results;
+ }
+
+ private function doInvokeFunc(FuncAddr $funcAddr): void
+ {
+ $fn = $this->store->funcs[$funcAddr->value];
+ if ($fn instanceof FuncInsts\Wasm) {
+ $this->doInvokeWasmFunc($fn);
+ } elseif ($fn instanceof FuncInsts\Host) {
+ $this->doInvokeHostFunc($fn);
+ } else {
+ throw new \RuntimeException("doInvokeFunc: unreachable");
+ }
+ }
+
+ private function doInvokeWasmFunc(FuncInsts\Wasm $fn): void
+ {
+ $paramTypes = $fn->type->params->types;
+ $n = count($paramTypes);
+ $resultTypes = $fn->type->results->types;
+ $m = count($resultTypes);
+ $ts = $fn->code->locals;
+ $instrs = $fn->code->body->instrs;
+ array_pop($instrs); // drop "end"
+ $vals = [];
+ for ($i = 0; $i < $n; $i++) {
+ $vals[] = $this->stack->popValue();
+ }
+ $f = StackEntry::Frame(
+ $m,
+ array_map(fn ($local) => self::defaultValueFromValType($local->type), $ts),
+ $fn->module,
+ );
+ $this->activateFrame($f);
+ $l = StackEntry::Label($m);
+ $this->execInstrs($instrs, $l);
+ $this->deactivateFrame($m);
+ }
+
+ private function activateFrame(StackEntries\Frame $f): void
+ {
+ $this->stack->push($f);
+ }
+
+ private function deactivateFrame(int $arity): void
+ {
+ $vals = [];
+ for ($i = 0; $i < $arity; $i++) {
+ $vals[] = $this->stack->popValue();
+ }
+ $this->stack->popEntriesToCurrentFrame();
+ for ($i = $arity - 1; 0 <= $i; $i--) {
+ $this->stack->pushValue($vals[$i]);
+ }
+ }
+
+ private function activateLabel(StackEntries\Label $l): void
+ {
+ $this->stack->push($l);
+ }
+
+ private function deactivateLabel(?int $arity): void
+ {
+ if ($arity === null) {
+ $vals = $this->stack->popValuesToLabel();
+ } else {
+ $vals = $this->stack->popNValues($arity);
+ $this->stack->popValuesToLabel();
+ }
+ for ($i = count($vals) - 1; 0 <= $i; $i--) {
+ $this->stack->pushValue($vals[$i]);
+ }
+ }
+
+ private function doInvokeHostFunc(FuncInsts\Host $f): void
+ {
+ ($f->callback)();
}
/**
* @param list<Instr> $instrs
*/
- private function execInstrs(array $instrs): void
+ private function execInstrs(
+ array $instrs,
+ StackEntries\Label $l,
+ ): ?ControlFlowResult {
+ $this->activateLabel($l);
+
+ foreach ($instrs as $i) {
+ $result = $this->execInstr($i);
+ if ($result !== null) {
+ return $result;
+ }
+ }
+
+ $this->deactivateLabel(null);
+ return null;
+ }
+
+ /**
+ * @param list<Instr> $instrs
+ */
+ private function execInstrsForInit(array $instrs): void
{
foreach ($instrs as $i) {
- $this->interpret($i);
+ $this->execInstr($i);
}
}
/**
* @param list<Instr> $instrs
*/
- private function evalInstrs(array $instrs): Val
+ private function evalInstrsForInit(array $instrs): Val
{
- $this->execInstrs($instrs);
+ $this->execInstrsForInit($instrs);
$result = $this->stack->pop();
assert($result instanceof StackEntries\Value);
return $result->inner;
}
- private function interpret(Instr $instr): void
+ private function execInstr(Instr $instr): ?ControlFlowResult
{
if ($instr instanceof Instrs\Numeric\F32Abs) {
- $v = $this->stack->pop();
- assert($v instanceof StackEntries\Value);
- assert($v->inner instanceof Vals\Num);
- assert($v->inner->inner instanceof Nums\F32_);
- $this->stack->push(StackEntry::Value(Val::Num(Num::F32($v->inner->inner))));
+ $v = $this->stack->popF32();
+ $this->stack->pushF32(abs($v));
} elseif ($instr instanceof Instrs\Numeric\F32Add) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Add: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Ceil) {
- assert(false, "not implemented " . $instr::class);
+ $v = $this->stack->popF32();
+ $this->stack->pushF32(ceil($v));
} elseif ($instr instanceof Instrs\Numeric\F32Const) {
- $this->stack->push(StackEntry::Value(Val::Num(Num::F32($instr->value))));
+ $this->stack->pushValue(Val::NumF32($instr->value));
} elseif ($instr instanceof Instrs\Numeric\F32ConvertI32S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32ConvertI32S)\n";
} elseif ($instr instanceof Instrs\Numeric\F32ConvertI32U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32ConvertI32U)\n";
} elseif ($instr instanceof Instrs\Numeric\F32ConvertI64S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32ConvertI64S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32ConvertI64U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32ConvertI64U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32CopySign) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32CopySign: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32DemoteF64) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32DemoteF64)\n";
} elseif ($instr instanceof Instrs\Numeric\F32Div) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Div)\n";
} elseif ($instr instanceof Instrs\Numeric\F32Eq) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Eq: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Floor) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Floor: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Ge) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Ge: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Gt) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Gt: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Le) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Le: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Lt) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Lt)\n";
} elseif ($instr instanceof Instrs\Numeric\F32Max) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Max: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Min) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Min: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Mul) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Mul)\n";
} elseif ($instr instanceof Instrs\Numeric\F32Ne) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Ne)\n";
} elseif ($instr instanceof Instrs\Numeric\F32Nearest) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Nearest: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Neg) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Neg: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32ReinterpretI32) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32ReinterpretI32)\n";
} elseif ($instr instanceof Instrs\Numeric\F32ReinterpretI64) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32ReinterpretI64: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Sqrt) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Sqrt: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Sub) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Sub: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F32Trunc) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F32Trunc: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F64Abs) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Abs)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Add) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Add)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Ceil) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Ceil)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Const) {
- $this->stack->push(StackEntry::Value(Val::Num(Num::F64($instr->value))));
+ $this->stack->pushValue(Val::NumF64($instr->value));
} elseif ($instr instanceof Instrs\Numeric\F64ConvertI32S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64ConvertI32S)\n";
} elseif ($instr instanceof Instrs\Numeric\F64ConvertI32U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64ConvertI32U)\n";
} elseif ($instr instanceof Instrs\Numeric\F64ConvertI64S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64ConvertI64S)\n";
} elseif ($instr instanceof Instrs\Numeric\F64ConvertI64U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64ConvertI64U)\n";
} elseif ($instr instanceof Instrs\Numeric\F64CopySign) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64CopySign)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Div) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Div)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Eq) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Eq)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Floor) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Floor)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Ge) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Ge)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Gt) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Gt)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Le) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Le)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Lt) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Lt)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Max) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Max)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Min) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F64Min: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F64Mul) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Mul)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Ne) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Ne)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Nearest) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F64Nearest: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F64Neg) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Neg)\n";
} elseif ($instr instanceof Instrs\Numeric\F64PromoteF32) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64PromoteF32)\n";
} elseif ($instr instanceof Instrs\Numeric\F64ReinterpretI32) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F64ReinterpretI32: not implemented");
} elseif ($instr instanceof Instrs\Numeric\F64ReinterpretI64) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64ReinterpretI64)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Sqrt) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Sqrt)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Sub) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Sub)\n";
} elseif ($instr instanceof Instrs\Numeric\F64Trunc) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("F64Trunc: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32Add) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Add)\n";
} elseif ($instr instanceof Instrs\Numeric\I32And) {
- assert(false, "not implemented " . $instr::class);
+ $c2 = $this->stack->popI32();
+ $c1 = $this->stack->popI32();
+ $this->stack->pushI32($c1 & $c2);
} elseif ($instr instanceof Instrs\Numeric\I32Clz) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Clz)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Const) {
- $this->stack->push(StackEntry::Value(Val::Num(Num::I32($instr->value))));
+ $this->stack->pushValue(Val::NumI32($instr->value));
} elseif ($instr instanceof Instrs\Numeric\I32Ctz) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Ctz)\n";
} elseif ($instr instanceof Instrs\Numeric\I32DivS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32DivS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32DivU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32DivU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Eq) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Eq)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Eqz) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Eqz)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Extend16S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Extend16S)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Extend8S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Extend8S)\n";
} elseif ($instr instanceof Instrs\Numeric\I32GeS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32GeS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32GeU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32GeU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32GtS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32GtS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32GtU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32GtU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32LeS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32LeS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32LeU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32LeU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32LtS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32LtS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32LtU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32LtU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Mul) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Mul)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Ne) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Ne)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Or) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Or)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Popcnt) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Popcnt)\n";
} elseif ($instr instanceof Instrs\Numeric\I32ReinterpretF32) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32ReinterpretF32)\n";
} elseif ($instr instanceof Instrs\Numeric\I32ReinterpretF64) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32ReinterpretF64: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32RemS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32RemS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32RemU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32RemU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32RotL) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32RotL)\n";
} elseif ($instr instanceof Instrs\Numeric\I32RotR) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32RotR)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Shl) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Shl)\n";
} elseif ($instr instanceof Instrs\Numeric\I32ShrS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32ShrS)\n";
} elseif ($instr instanceof Instrs\Numeric\I32ShrU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32ShrU)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Sub) {
- assert(false, "not implemented " . $instr::class);
+ $c2 = $this->stack->popI32();
+ $c1 = $this->stack->popI32();
+ $this->stack->pushI32($c1 - $c2);
} elseif ($instr instanceof Instrs\Numeric\I32TruncF32S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32TruncF32S)\n";
} elseif ($instr instanceof Instrs\Numeric\I32TruncF32U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32TruncF32U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32TruncF64S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32TruncF64S)\n";
} elseif ($instr instanceof Instrs\Numeric\I32TruncF64U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32TruncF64U)\n";
} elseif ($instr instanceof Instrs\Numeric\I32TruncSatF32S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32TruncSatF32S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32TruncSatF32U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32TruncSatF32U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32TruncSatF64S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32TruncSatF64S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32TruncSatF64U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I32TruncSatF64U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I32WrapI64) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32WrapI64)\n";
} elseif ($instr instanceof Instrs\Numeric\I32Xor) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Xor)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Add) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Add)\n";
} elseif ($instr instanceof Instrs\Numeric\I64And) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64And)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Clz) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Clz)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Const) {
- $this->stack->push(StackEntry::Value(Val::Num(Num::I64($instr->value))));
+ $this->stack->pushValue(Val::NumI64($instr->value));
} elseif ($instr instanceof Instrs\Numeric\I64Ctz) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64Ctz: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64DivS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64DivS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64DivU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64DivU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Eq) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Eq)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Eqz) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Eqz)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Extend16S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Extend16S)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Extend32S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64Extend32S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64Extend8S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64Extend8S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64ExtendI32S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64ExtendI32S)\n";
} elseif ($instr instanceof Instrs\Numeric\I64ExtendI32U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64ExtendI32U)\n";
} elseif ($instr instanceof Instrs\Numeric\I64GeS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64GeS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64GeU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64GeU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64GtS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64GtS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64GtU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64GtU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64LeS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64LeS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64LeU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64LeU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64LtS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64LtS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64LtU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64LtU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Mul) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Mul)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Ne) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Ne)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Or) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Or)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Popcnt) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64Popcnt: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64ReinterpretF32) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64ReinterpretF32: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64ReinterpretF64) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64ReinterpretF64)\n";
} elseif ($instr instanceof Instrs\Numeric\I64RemS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64RemS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64RemU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64RemU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64RotL) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64RotL)\n";
} elseif ($instr instanceof Instrs\Numeric\I64RotR) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64RotR)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Shl) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Shl)\n";
} elseif ($instr instanceof Instrs\Numeric\I64ShrS) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64ShrS)\n";
} elseif ($instr instanceof Instrs\Numeric\I64ShrU) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64ShrU)\n";
} elseif ($instr instanceof Instrs\Numeric\I64Sub) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Sub)\n";
} elseif ($instr instanceof Instrs\Numeric\I64TruncF32S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncF32S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64TruncF32U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncF32U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64TruncF64S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64TruncF64S)\n";
} elseif ($instr instanceof Instrs\Numeric\I64TruncF64U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64TruncF64U)\n";
} elseif ($instr instanceof Instrs\Numeric\I64TruncSatF32S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncSatF32S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64TruncSatF32U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncSatF32U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64TruncSatF64S) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncSatF64S: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64TruncSatF64U) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("I64TruncSatF64U: not implemented");
} elseif ($instr instanceof Instrs\Numeric\I64Xor) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Xor)\n";
} elseif ($instr instanceof Instrs\Reference\RefFunc) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->func;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->funcAddrs[$x->value];
+ $this->stack->pushRefFunc($a);
} elseif ($instr instanceof Instrs\Reference\RefIsNull) {
- assert(false, "not implemented " . $instr::class);
+ $val = $this->stack->popRef();
+ $this->stack->pushBool($val instanceof Refs\RefNull);
} elseif ($instr instanceof Instrs\Reference\RefNull) {
- assert(false, "not implemented " . $instr::class);
+ $t = $instr->type;
+ $this->stack->pushRefNull($t);
} elseif ($instr instanceof Instrs\Parametric\Drop) {
- assert(false, "not implemented " . $instr::class);
+ $this->stack->pop();
} elseif ($instr instanceof Instrs\Parametric\Select) {
- assert(false, "not implemented " . $instr::class);
+ $c = $this->stack->popI32();
+ $val2 = $this->stack->pop();
+ assert($val2 !== null);
+ $val1 = $this->stack->pop();
+ assert($val1 !== null);
+ if ($c !== 0) {
+ $this->stack->push($val1);
+ } else {
+ $this->stack->push($val2);
+ }
} elseif ($instr instanceof Instrs\Variable\GlobalGet) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->var;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->globalAddrs[$x->value];
+ $glob = $this->store->globals[$a->value];
+ $val = $glob->value;
+ $this->stack->pushValue($val);
} elseif ($instr instanceof Instrs\Variable\GlobalSet) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->var;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->globalAddrs[$x->value];
+ $glob = $this->store->globals[$a->value];
+ $val = $this->stack->popValue();
+ $glob->value = $val;
} elseif ($instr instanceof Instrs\Variable\LocalGet) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->var;
+ $f = $this->stack->currentFrame();
+ $val = $f->locals[$x->value];
+ $this->stack->pushValue($val);
} elseif ($instr instanceof Instrs\Variable\LocalSet) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->var;
+ $f = $this->stack->currentFrame();
+ $val = $this->stack->popValue();
+ // @phpstan-ignore-next-line
+ $f->locals[$x->value] = $val;
} elseif ($instr instanceof Instrs\Variable\LocalTee) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->var;
+ $f = $this->stack->currentFrame();
+ $val = $this->stack->popValue();
+ // @phpstan-ignore-next-line
+ $f->locals[$x->value] = $val;
+ $this->stack->pushValue($val);
} elseif ($instr instanceof Instrs\Table\ElemDrop) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->elem;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->elemAddrs[$x->value];
+ $elem = $this->store->elems[$a->value];
+ // @phpstan-ignore-next-line
+ $this->store->elems[$a->value] = new ElemInst($elem->type, []);
} elseif ($instr instanceof Instrs\Table\TableCopy) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("TableCopy: not implemented");
} elseif ($instr instanceof Instrs\Table\TableFill) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("TableFill: not implemented");
} elseif ($instr instanceof Instrs\Table\TableGet) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("TableGet: not implemented");
} elseif ($instr instanceof Instrs\Table\TableGrow) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("TableGrow: not implemented");
} elseif ($instr instanceof Instrs\Table\TableInit) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->to;
+ $y = $instr->from;
+ $f = $this->stack->currentFrame();
+ $ta = $f->module->tableAddrs[$x->value];
+ $tab = $this->store->tables[$ta->value];
+ $ea = $f->module->elemAddrs[$y->value];
+ $elem = $this->store->elems[$ea->value];
+ $n = $this->stack->popI32();
+ $s = $this->stack->popI32();
+ $d = $this->stack->popI32();
+ if (count($elem->elem) < $s + $n) {
+ throw new TrapException("table.init: out of bounds");
+ }
+ if (count($tab->elem) < $d + $n) {
+ throw new TrapException("table.init: out of bounds");
+ }
+ for ($i = 0; $i < $n; $i++) {
+ $val = $elem->elem[$s];
+ $this->stack->pushI32($d);
+ $this->stack->pushValue(Val::Ref($val));
+ $this->execInstr(Instr::TableSet(new TableIdx($x->value)));
+ $d++;
+ $s++;
+ }
} elseif ($instr instanceof Instrs\Table\TableSet) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->table;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->tableAddrs[$x->value];
+ $tab = $this->store->tables[$a->value];
+ $val = $this->stack->popValue();
+ $i = $this->stack->popI32();
+ if (count($tab->elem) <= $i) {
+ throw new TrapException("table.set: out of bounds");
+ }
+ // @phpstan-ignore-next-line
+ $tab->elem[$i] = $val;
} elseif ($instr instanceof Instrs\Table\TableSize) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("TableSize: not implemented");
} elseif ($instr instanceof Instrs\Memory\DataDrop) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->data;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->dataAddrs[$x->value];
+ // @phpstan-ignore-next-line
+ $this->store->datas[$a->value] = new DataInst([]);
} elseif ($instr instanceof Instrs\Memory\F32Load) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Load)\n";
} elseif ($instr instanceof Instrs\Memory\F32Store) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F32Store)\n";
} elseif ($instr instanceof Instrs\Memory\F64Load) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Load)\n";
} elseif ($instr instanceof Instrs\Memory\F64Store) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (F64Store)\n";
} elseif ($instr instanceof Instrs\Memory\I32Load) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Load)\n";
} elseif ($instr instanceof Instrs\Memory\I32Load16S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Load16S)\n";
} elseif ($instr instanceof Instrs\Memory\I32Load16U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Load16U)\n";
} elseif ($instr instanceof Instrs\Memory\I32Load8S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Load8S)\n";
} elseif ($instr instanceof Instrs\Memory\I32Load8U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Load8U)\n";
} elseif ($instr instanceof Instrs\Memory\I32Store) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Store)\n";
} elseif ($instr instanceof Instrs\Memory\I32Store16) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I32Store16)\n";
} elseif ($instr instanceof Instrs\Memory\I32Store8) {
- assert(false, "not implemented " . $instr::class);
+ $offset = $instr->offset;
+ $align = $instr->align;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->memAddrs[0];
+ $mem = $this->store->mems[$a->value];
+ $c = $this->stack->popI32();
+ $i = $this->stack->popI32();
+ $ea = $i + $offset;
+ if (count($mem->data) < $ea + 1) {
+ throw new TrapException("i32.store8: out of bounds");
+ }
+ $n = $c & 0xFF;
+ // @phpstan-ignore-next-line
+ $mem->data[$ea] = $n;
} elseif ($instr instanceof Instrs\Memory\I64Load) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load16S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load16S)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load16U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load16U)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load32S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load32S)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load32U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load32U)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load8S) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load8S)\n";
} elseif ($instr instanceof Instrs\Memory\I64Load8U) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Load8U)\n";
} elseif ($instr instanceof Instrs\Memory\I64Store) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Store)\n";
} elseif ($instr instanceof Instrs\Memory\I64Store16) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Store16)\n";
} elseif ($instr instanceof Instrs\Memory\I64Store32) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Store32)\n";
} elseif ($instr instanceof Instrs\Memory\I64Store8) {
- assert(false, "not implemented " . $instr::class);
+ echo "TRACE (I64Store8)\n";
} elseif ($instr instanceof Instrs\Memory\MemoryCopy) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("MemoryCopy: not implemented");
} elseif ($instr instanceof Instrs\Memory\MemoryFill) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("MemoryFill: not implemented");
} elseif ($instr instanceof Instrs\Memory\MemoryGrow) {
- assert(false, "not implemented " . $instr::class);
+ throw new \RuntimeException("MemoryGrow: not implemented");
} elseif ($instr instanceof Instrs\Memory\MemoryInit) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->data;
+ $f = $this->stack->currentFrame();
+ $ma = $f->module->memAddrs[0];
+ $mem = $this->store->mems[$ma->value];
+ $da = $f->module->dataAddrs[$x->value];
+ $data = $this->store->datas[$da->value];
+ $n = $this->stack->popI32();
+ $s = $this->stack->popI32();
+ $d = $this->stack->popI32();
+ if (count($data->data) < $s + $n) {
+ throw new TrapException("memory.init: out of bounds");
+ }
+ if (count($mem->data) < $d + $n) {
+ throw new TrapException("memory.init: out of bounds");
+ }
+ for ($i = 0; $i < $n; $i++) {
+ $b = $data->data[$s];
+ $this->stack->pushI32($d);
+ $this->stack->pushI32($b);
+ $this->execInstr(Instr::I32Store8(0, 0));
+ $d++;
+ $s++;
+ }
} elseif ($instr instanceof Instrs\Memory\MemorySize) {
- assert(false, "not implemented " . $instr::class);
+ $f = $this->stack->currentFrame();
+ $a = $f->module->memAddrs[0];
+ $mem = $this->store->mems[$a->value];
+ $szInByte = count($mem->data);
+ assert(is_int($szInByte / (64 * 1024)));
+ $sz = $szInByte / (64 * 1024);
+ $this->stack->pushI32($sz);
} elseif ($instr instanceof Instrs\Control\Block) {
- assert(false, "not implemented " . $instr::class);
+ $blockType = $instr->type;
+ $instrs = $instr->body;
+ $f = $this->stack->currentFrame();
+ $bt = self::expandBlockType($blockType, $f->module);
+ $n = count($bt->results->types);
+ $l = StackEntry::Label($n);
+ $result = $this->execInstrs($instrs, $l);
+ if ($result === null) {
+ // Do nothing.
+ } elseif ($result instanceof ControlFlowResults\Return_) {
+ return $result;
+ } elseif ($result instanceof ControlFlowResults\Br) {
+ if ($result->label->value === 0) {
+ $this->deactivateLabel($n);
+ } else {
+ $this->deactivateLabel($n);
+ return ControlFlowResult::Br(new LabelIdx($result->label->value - 1));
+ }
+ } else {
+ throw new \RuntimeException("doInvokeWasmFunc: unreachable");
+ }
} elseif ($instr instanceof Instrs\Control\Br) {
- assert(false, "not implemented " . $instr::class);
+ $l = $instr->label;
+ return ControlFlowResult::Br($l);
} elseif ($instr instanceof Instrs\Control\BrIf) {
- assert(false, "not implemented " . $instr::class);
+ $l = $instr->label;
+ $c = $this->stack->popI32();
+ if ($c !== 0) {
+ return $this->execInstr(Instr::Br($l));
+ }
} elseif ($instr instanceof Instrs\Control\BrTable) {
- assert(false, "not implemented " . $instr::class);
+ $ls = $instr->labelTable;
+ $ln = $instr->defaultLabel;
+ $i = $this->stack->popI32();
+ if ($i < count($ls)) {
+ return $this->execInstr(Instr::Br($ls[$i]));
+ } else {
+ return $this->execInstr(Instr::Br($ln));
+ }
} elseif ($instr instanceof Instrs\Control\Call) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->func;
+ $f = $this->stack->currentFrame();
+ $a = $f->module->funcAddrs[$x->value];
+ $this->doInvokeFunc($a);
} elseif ($instr instanceof Instrs\Control\CallIndirect) {
- assert(false, "not implemented " . $instr::class);
+ $x = $instr->funcTable;
+ $y = $instr->type;
+ $f = $this->stack->currentFrame();
+ $ta = $f->module->tableAddrs[$x->value];
+ $tab = $this->store->tables[$ta->value];
+ $ftExpect = $f->module->types[$y->value];
+ $i = $this->stack->popI32();
+ if (count($tab->elem) <= $i) {
+ throw new TrapException("call_indirect: out of bounds");
+ }
+ $r = $tab->elem[$i];
+ if ($r instanceof Refs\RefNull) {
+ throw new TrapException("call_indirect: ref.null");
+ }
+ assert($r instanceof Refs\RefFunc);
+ $a = $r->addr;
+ $fn = $this->store->funcs[$a->value];
+ assert($fn instanceof FuncInsts\Wasm || $fn instanceof FuncInsts\Host);
+ $ftActual = $fn->type;
+ if ($ftExpect->equals($ftActual)) {
+ throw new TrapException("call_indirect: type mismatch");
+ }
+ $this->doInvokeFunc($a);
} elseif ($instr instanceof Instrs\Control\Else_) {
- assert(false, "not implemented " . $instr::class);
+ // Do nothing.
} elseif ($instr instanceof Instrs\Control\End) {
- assert(false, "not implemented " . $instr::class);
+ // Do nothing.
} elseif ($instr instanceof Instrs\Control\If_) {
- assert(false, "not implemented " . $instr::class);
+ $blockType = $instr->type;
+ $instrs1 = $instr->thenBody;
+ $instrs2 = $instr->elseBody;
+ $c = $this->stack->popI32();
+ if ($c !== 0) {
+ return $this->execInstr(Instr::Block($blockType, $instrs1));
+ } else {
+ return $this->execInstr(Instr::Block($blockType, $instrs2));
+ }
} elseif ($instr instanceof Instrs\Control\Loop) {
- assert(false, "not implemented " . $instr::class);
+ $blockType = $instr->type;
+ $instrs = $instr->body;
+ $f = $this->stack->currentFrame();
+ $bt = self::expandBlockType($blockType, $f->module);
+ $n = count($bt->results->types);
+ $l = StackEntry::Label($n);
+ while (true) {
+ $result = $this->execInstrs($instrs, $l);
+ if ($result === null) {
+ break;
+ } elseif ($result instanceof ControlFlowResults\Return_) {
+ return $result;
+ } elseif ($result instanceof ControlFlowResults\Br) {
+ if ($result->label->value === 0) {
+ $this->deactivateLabel($n);
+ continue;
+ } else {
+ $this->deactivateLabel($n);
+ return ControlFlowResult::Br(new LabelIdx($result->label->value - 1));
+ }
+ } else {
+ throw new \RuntimeException("doInvokeWasmFunc: unreachable");
+ }
+ }
} elseif ($instr instanceof Instrs\Control\Nop) {
- assert(false, "not implemented " . $instr::class);
+ // Do nothing.
} elseif ($instr instanceof Instrs\Control\Return_) {
- assert(false, "not implemented " . $instr::class);
+ return ControlFlowResult::Return();
} elseif ($instr instanceof Instrs\Control\Unreachable) {
- assert(false, "not implemented " . $instr::class);
+ throw new TrapException("unreachable");
+ } else {
+ throw new \RuntimeException("invalid instruction");
+ }
+ return null;
+ }
+
+ private static function defaultValueFromValType(ValType $type): Val
+ {
+ return match ($type::class) {
+ ValTypes\NumType::class => match ($type->inner) {
+ NumType::I32 => Val::NumI32(0),
+ NumType::I64 => Val::NumI64(0),
+ NumType::F32 => Val::NumF32(0),
+ NumType::F64 => Val::NumF64(0),
+ },
+ ValTypes\RefType::class => Val::RefNull($type->inner),
+ default => throw new \RuntimeException("unreachable"),
+ };
+ }
+
+ private static function expandBlockType(BlockType $bt, ModuleInst $module): FuncType
+ {
+ if ($bt instanceof BlockTypes\TypeIdx) {
+ return $module->types[$bt->inner->value];
+ } elseif ($bt instanceof BlockTypes\ValType) {
+ $t = $bt->inner;
+ return new FuncType(
+ new ResultType([]),
+ new ResultType($t === null ? [] : [$t]),
+ );
} else {
- assert(false);
+ throw new \RuntimeException("expand(): invalid blocktype");
}
}
}
diff --git a/src/Execution/Stack.php b/src/Execution/Stack.php
index 1fa2dae..439726d 100644
--- a/src/Execution/Stack.php
+++ b/src/Execution/Stack.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Nsfisis\Waddiwasi\Execution;
+use Nsfisis\Waddiwasi\Structure\Types\RefType;
+
final class Stack
{
/**
@@ -19,11 +21,165 @@ final class Stack
$this->entries[] = $entry;
}
+ public function pushValue(Val $val): void
+ {
+ $this->push(StackEntry::Value($val));
+ }
+
+ public function pushBool(bool $value): void
+ {
+ $this->pushValue(Val::NumI32((int)$value));
+ }
+
+ /**
+ * @param S32 $value
+ */
+ public function pushI32(int $value): void
+ {
+ $this->pushValue(Val::NumI32($value));
+ }
+
+ /**
+ * @param S64 $value
+ */
+ public function pushI64(int $value): void
+ {
+ $this->pushValue(Val::NumI64($value));
+ }
+
+ /**
+ * @param F32 $value
+ */
+ public function pushF32(float $value): void
+ {
+ $this->pushValue(Val::NumF32($value));
+ }
+
+ /**
+ * @param F64 $value
+ */
+ public function pushF64(float $value): void
+ {
+ $this->pushValue(Val::NumF64($value));
+ }
+
+ public function pushRefNull(RefType $type): void
+ {
+ $this->pushValue(Val::RefNull($type));
+ }
+
+ public function pushRefFunc(FuncAddr $addr): void
+ {
+ $this->pushValue(Val::RefFunc($addr));
+ }
+
+ public function pushRefExtern(ExternAddr $addr): void
+ {
+ $this->pushValue(Val::RefExtern($addr));
+ }
+
public function pop(): ?StackEntry
{
return array_pop($this->entries);
}
+ public function popValue(): Val
+ {
+ $result = $this->pop();
+ assert($result instanceof StackEntries\Value);
+ return $result->inner;
+ }
+
+ /**
+ * @return list<Val>
+ */
+ public function popNValues(int $n): array
+ {
+ $results = [];
+ for ($i = 0; $i < $n; $i++) {
+ $results[] = $this->popValue();
+ }
+ return $results;
+ }
+
+ /**
+ * @return S32
+ */
+ public function popI32(): int
+ {
+ $v = $this->popValue();
+ assert($v instanceof Vals\Num);
+ assert($v->inner instanceof Nums\I32);
+ return $v->inner->value;
+ }
+
+ /**
+ * @return S64
+ */
+ public function popI64(): int
+ {
+ $v = $this->popValue();
+ assert($v instanceof Vals\Num);
+ assert($v->inner instanceof Nums\I64);
+ return $v->inner->value;
+ }
+
+ /**
+ * @return F32
+ */
+ public function popF32(): float
+ {
+ $v = $this->popValue();
+ assert($v instanceof Vals\Num);
+ assert($v->inner instanceof Nums\F32_);
+ return $v->inner->value;
+ }
+
+ /**
+ * @return F64
+ */
+ public function popF64(): float
+ {
+ $v = $this->popValue();
+ assert($v instanceof Vals\Num);
+ assert($v->inner instanceof Nums\F64_);
+ return $v->inner->value;
+ }
+
+ public function popRef(): Ref
+ {
+ $v = $this->popValue();
+ assert($v instanceof Vals\Ref);
+ return $v->inner;
+ }
+
+ /**
+ * @return list<Val>
+ */
+ public function popValuesToLabel(): array
+ {
+ $results = [];
+ while (!$this->isEmpty()) {
+ $top = $this->pop();
+ if ($top instanceof StackEntries\Value) {
+ $results[] = $top->inner;
+ } else {
+ assert($top instanceof StackEntries\Label);
+ break;
+ }
+ }
+ return $results;
+ }
+
+ public function popEntriesToCurrentFrame(): void
+ {
+ while (!$this->isEmpty()) {
+ if ($this->pop() instanceof StackEntries\Frame) {
+ return;
+ }
+ }
+ }
+
public function top(): ?StackEntry
{
$n = array_key_last($this->entries);
@@ -39,4 +195,14 @@ final class Stack
{
return $this->count() === 0;
}
+
+ public function currentFrame(): StackEntries\Frame
+ {
+ for ($i = count($this->entries) - 1; 0 <= $i; $i--) {
+ if ($this->entries[$i] instanceof StackEntries\Frame) {
+ return $this->entries[$i];
+ }
+ }
+ throw new \RuntimeException('No frame found');
+ }
}
diff --git a/src/Execution/StackEntries/Frame.php b/src/Execution/StackEntries/Frame.php
index f8bd68e..c1a876e 100644
--- a/src/Execution/StackEntries/Frame.php
+++ b/src/Execution/StackEntries/Frame.php
@@ -8,16 +8,16 @@ use Nsfisis\Waddiwasi\Execution\ModuleInst;
use Nsfisis\Waddiwasi\Execution\StackEntry;
use Nsfisis\Waddiwasi\Execution\Val;
-final readonly class Frame extends StackEntry
+final class Frame extends StackEntry
{
/**
* @param int<0, max> $arity
* @param list<Val> $locals
*/
public function __construct(
- public int $arity,
+ public readonly int $arity,
public array $locals,
- public ModuleInst $module,
+ public readonly ModuleInst $module,
) {
}
}
diff --git a/src/Execution/StackEntries/Label.php b/src/Execution/StackEntries/Label.php
index b83fb0f..e6b9954 100644
--- a/src/Execution/StackEntries/Label.php
+++ b/src/Execution/StackEntries/Label.php
@@ -5,17 +5,14 @@ declare(strict_types=1);
namespace Nsfisis\Waddiwasi\Execution\StackEntries;
use Nsfisis\Waddiwasi\Execution\StackEntry;
-use Nsfisis\Waddiwasi\Structure\Instructions\Instr;
-final readonly class Label extends StackEntry
+final class Label extends StackEntry
{
/**
* @param int<0, max> $arity
- * @param list<Instr> $target
*/
public function __construct(
- public int $arity,
- public array $target,
+ public readonly int $arity,
) {
}
}
diff --git a/src/Execution/StackEntries/Value.php b/src/Execution/StackEntries/Value.php
index 1cd1146..a0f621a 100644
--- a/src/Execution/StackEntries/Value.php
+++ b/src/Execution/StackEntries/Value.php
@@ -7,10 +7,10 @@ namespace Nsfisis\Waddiwasi\Execution\StackEntries;
use Nsfisis\Waddiwasi\Execution\StackEntry;
use Nsfisis\Waddiwasi\Execution\Val;
-final readonly class Value extends StackEntry
+final class Value extends StackEntry
{
public function __construct(
- public Val $inner,
+ public readonly Val $inner,
) {
}
}
diff --git a/src/Execution/StackEntry.php b/src/Execution/StackEntry.php
index 8a9d7dd..ec2fe7a 100644
--- a/src/Execution/StackEntry.php
+++ b/src/Execution/StackEntry.php
@@ -4,9 +4,7 @@ declare(strict_types=1);
namespace Nsfisis\Waddiwasi\Execution;
-use Nsfisis\Waddiwasi\Structure\Instructions\Instr;
-
-abstract readonly class StackEntry
+abstract class StackEntry
{
final public static function Value(Val $inner): StackEntries\Value
{
@@ -15,13 +13,11 @@ abstract readonly class StackEntry
/**
* @param int<0, max> $arity
- * @param list<Instr> $target
*/
final public static function Label(
int $arity,
- array $target,
): StackEntries\Label {
- return new StackEntries\Label($arity, $target);
+ return new StackEntries\Label($arity);
}
/**
diff --git a/src/Execution/TableInst.php b/src/Execution/TableInst.php
index 5a6766d..0075acd 100644
--- a/src/Execution/TableInst.php
+++ b/src/Execution/TableInst.php
@@ -6,13 +6,13 @@ namespace Nsfisis\Waddiwasi\Execution;
use Nsfisis\Waddiwasi\Structure\Types\TableType;
-final readonly class TableInst
+final class TableInst
{
/**
* @param list<Ref> $elem
*/
public function __construct(
- public TableType $type,
+ public readonly TableType $type,
public array $elem,
) {
}
diff --git a/src/Execution/TrapException.php b/src/Execution/TrapException.php
new file mode 100644
index 0000000..2948e8d
--- /dev/null
+++ b/src/Execution/TrapException.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nsfisis\Waddiwasi\Execution;
+
+use RuntimeException;
+
+class TrapException extends RuntimeException
+{
+}
diff --git a/src/Execution/Val.php b/src/Execution/Val.php
index 287f441..0f9ce42 100644
--- a/src/Execution/Val.php
+++ b/src/Execution/Val.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Nsfisis\Waddiwasi\Execution;
+use Nsfisis\Waddiwasi\Structure\Types\RefType;
+
abstract readonly class Val
{
final public static function Num(Num $inner): Vals\Num
@@ -11,8 +13,55 @@ abstract readonly class Val
return new Vals\Num($inner);
}
+ /**
+ * @param S32 $value
+ */
+ final public static function NumI32(int $value): Vals\Num
+ {
+ return self::Num(Num::I32($value));
+ }
+
+ /**
+ * @param S64 $value
+ */
+ final public static function NumI64(int $value): Vals\Num
+ {
+ return self::Num(Num::I64($value));
+ }
+
+ /**
+ * @param F32 $value
+ */
+ final public static function NumF32(float $value): Vals\Num
+ {
+ return self::Num(Num::F32($value));
+ }
+
+ /**
+ * @param F64 $value
+ */
+ final public static function NumF64(float $value): Vals\Num
+ {
+ return self::Num(Num::F64($value));
+ }
+
final public static function Ref(Ref $inner): Vals\Ref
{
return new Vals\Ref($inner);
}
+
+ final public static function RefNull(RefType $type): Vals\Ref
+ {
+ return self::Ref(Ref::RefNull($type));
+ }
+
+ final public static function RefFunc(FuncAddr $addr): Vals\Ref
+ {
+ return self::Ref(Ref::RefFunc($addr));
+ }
+
+ final public static function RefExtern(ExternAddr $addr): Vals\Ref
+ {
+ return self::Ref(Ref::RefExtern($addr));
+ }
}
diff --git a/src/Structure/Types/FuncType.php b/src/Structure/Types/FuncType.php
index 17ef98a..e445512 100644
--- a/src/Structure/Types/FuncType.php
+++ b/src/Structure/Types/FuncType.php
@@ -7,8 +7,14 @@ namespace Nsfisis\Waddiwasi\Structure\Types;
final readonly class FuncType
{
public function __construct(
- public ResultType $args,
- public ResultType $returns,
+ public ResultType $params,
+ public ResultType $results,
) {
}
+
+ public function equals(FuncType $other): bool
+ {
+ return $this->params->equals($other->params)
+ && $this->results->equals($other->results);
+ }
}
diff --git a/src/Structure/Types/ResultType.php b/src/Structure/Types/ResultType.php
index 267916b..a826805 100644
--- a/src/Structure/Types/ResultType.php
+++ b/src/Structure/Types/ResultType.php
@@ -13,4 +13,17 @@ final readonly class ResultType
public array $types,
) {
}
+
+ public function equals(ResultType $other): bool
+ {
+ if (count($this->types) !== count($other->types)) {
+ return false;
+ }
+ foreach ($this->types as $i => $type) {
+ if (!$type->equals($other->types[$i])) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/Structure/Types/ValType.php b/src/Structure/Types/ValType.php
index 58bf32c..bf072e2 100644
--- a/src/Structure/Types/ValType.php
+++ b/src/Structure/Types/ValType.php
@@ -20,4 +20,6 @@ abstract readonly class ValType
{
return new ValTypes\RefType($type);
}
+
+ abstract public function equals(ValType $other): bool;
}
diff --git a/src/Structure/Types/ValTypes/NumType.php b/src/Structure/Types/ValTypes/NumType.php
index 3376d27..fcaa7e3 100644
--- a/src/Structure/Types/ValTypes/NumType.php
+++ b/src/Structure/Types/ValTypes/NumType.php
@@ -12,4 +12,10 @@ final readonly class NumType extends ValType
protected function __construct(public OrigNumType $inner)
{
}
+
+ public function equals(ValType $other): bool
+ {
+ return $other instanceof self
+ && $this->inner === $other->inner;
+ }
}
diff --git a/src/Structure/Types/ValTypes/RefType.php b/src/Structure/Types/ValTypes/RefType.php
index 720eb7d..fc48dfb 100644
--- a/src/Structure/Types/ValTypes/RefType.php
+++ b/src/Structure/Types/ValTypes/RefType.php
@@ -12,4 +12,10 @@ final readonly class RefType extends ValType
protected function __construct(public OrigRefType $inner)
{
}
+
+ public function equals(ValType $other): bool
+ {
+ return $other instanceof self
+ && $this->inner === $other->inner;
+ }
}
diff --git a/src/Structure/Types/ValTypes/VecType.php b/src/Structure/Types/ValTypes/VecType.php
index 8fb546c..a28081f 100644
--- a/src/Structure/Types/ValTypes/VecType.php
+++ b/src/Structure/Types/ValTypes/VecType.php
@@ -12,4 +12,10 @@ final readonly class VecType extends ValType
protected function __construct(public OrigVecType $inner)
{
}
+
+ public function equals(ValType $other): bool
+ {
+ return $other instanceof self
+ && $this->inner === $other->inner; // @phpstan-ignore-line
+ }
}