Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93dfff358d | ||
|
|
3ed5c0a847 | ||
|
|
ff65a0d5f0 | ||
|
|
76d7d737c0 |
No files matched your search
@@ -8,7 +8,34 @@ set LIB=%ROOT%lib
|
|||||||
set FPC=C:\Lazarus\fpc\3.2.2\bin\x86_64-win64\fpc.exe
|
set FPC=C:\Lazarus\fpc\3.2.2\bin\x86_64-win64\fpc.exe
|
||||||
|
|
||||||
set TESTFLAGS=
|
set TESTFLAGS=
|
||||||
|
set DO_CLEAN=0
|
||||||
|
set DO_RUN=0
|
||||||
|
|
||||||
|
:parse_args
|
||||||
|
if "%1"=="" goto args_done
|
||||||
if "%1"=="test" set TESTFLAGS=-dRUN_TESTS
|
if "%1"=="test" set TESTFLAGS=-dRUN_TESTS
|
||||||
|
if "%1"=="clean" set DO_CLEAN=1
|
||||||
|
if "%1"=="run" set DO_RUN=1
|
||||||
|
if "%1"=="help" (
|
||||||
|
echo Usage: build.bat [options]
|
||||||
|
echo Options:
|
||||||
|
echo test - Build with tests enabled
|
||||||
|
echo clean - Clean build outputs
|
||||||
|
echo run - Run the built executable after building
|
||||||
|
echo help - Show this help message
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
:args_done
|
||||||
|
|
||||||
|
if %DO_CLEAN%==1 (
|
||||||
|
echo Cleaning build outputs...
|
||||||
|
if exist "%BIN%" rd /s /q "%BIN%"
|
||||||
|
if exist "%LIB%" rd /s /q "%LIB%"
|
||||||
|
echo Clean complete.
|
||||||
|
if "%TESTFLAGS%"=="" if %DO_RUN%==0 exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
if not exist "%BIN%" mkdir "%BIN%"
|
if not exist "%BIN%" mkdir "%BIN%"
|
||||||
if not exist "%LIB%" mkdir "%LIB%"
|
if not exist "%LIB%" mkdir "%LIB%"
|
||||||
@@ -17,6 +44,7 @@ REM ── Lint ─────────────────────
|
|||||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%ROOT%lint.ps1" -SrcRoot "%SRC%"
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%ROOT%lint.ps1" -SrcRoot "%SRC%"
|
||||||
if %ERRORLEVEL% NEQ 0 exit /b 1
|
if %ERRORLEVEL% NEQ 0 exit /b 1
|
||||||
|
|
||||||
|
REM ── Build ────────────────────────────────────────────────────────────
|
||||||
"%FPC%" ^
|
"%FPC%" ^
|
||||||
%TESTFLAGS% ^
|
%TESTFLAGS% ^
|
||||||
-FE"%BIN%" ^
|
-FE"%BIN%" ^
|
||||||
@@ -32,6 +60,7 @@ if %ERRORLEVEL% NEQ 0 exit /b 1
|
|||||||
-Fu"%SRC%\wasm\test\infra" ^
|
-Fu"%SRC%\wasm\test\infra" ^
|
||||||
-Fu"%SRC%\wasm\types" ^
|
-Fu"%SRC%\wasm\types" ^
|
||||||
-Fu"%SRC%\wasm\vm" ^
|
-Fu"%SRC%\wasm\vm" ^
|
||||||
|
-Fu"%SRC%\wasm\vm\opcodes" ^
|
||||||
-gw3 ^
|
-gw3 ^
|
||||||
-o"%BIN%\WASURO.exe" ^
|
-o"%BIN%\WASURO.exe" ^
|
||||||
"%SRC%\project\WASURO.lpr"
|
"%SRC%\project\WASURO.lpr"
|
||||||
@@ -42,3 +71,9 @@ if %ERRORLEVEL% NEQ 0 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
echo Build succeeded: %BIN%\WASURO.exe
|
echo Build succeeded: %BIN%\WASURO.exe
|
||||||
|
|
||||||
|
REM ── Run ────────────────────────────────────────────────────────────
|
||||||
|
if %DO_RUN%==1 (
|
||||||
|
echo Running WASURO...
|
||||||
|
"%BIN%\WASURO.exe"
|
||||||
|
)
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
unit wasm.test.opcode.elseop;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
procedure run;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
wasm.types.builtin, wasm.types.context, wasm.types.stack, wasm.vm, wasm.test.framework;
|
||||||
|
|
||||||
|
procedure run;
|
||||||
|
var
|
||||||
|
code : array[0..20] of TWASMUInt8;
|
||||||
|
ctx : PWASMProcessContext;
|
||||||
|
begin
|
||||||
|
test_begin('opcode.else');
|
||||||
|
|
||||||
|
{ Test 1: else skips else-body when if-true path taken }
|
||||||
|
{ i32.const 1 if $40 i32.const 10 else i32.const 20 end }
|
||||||
|
{ When condition is true the if-true body runs and else skips to past end }
|
||||||
|
code[0] := $41; { i32.const 1 }
|
||||||
|
code[1] := $01;
|
||||||
|
code[2] := $04; { if }
|
||||||
|
code[3] := $40; { blocktype empty }
|
||||||
|
code[4] := $41; { i32.const 10 }
|
||||||
|
code[5] := $0A;
|
||||||
|
code[6] := $05; { else }
|
||||||
|
code[7] := $41; { i32.const 20 }
|
||||||
|
code[8] := $14;
|
||||||
|
code[9] := $0B; { end }
|
||||||
|
ctx := make_test_context(@code[0], 10);
|
||||||
|
while wasm.vm.tick(ctx) do;
|
||||||
|
assert_i32('true path skips else body', wasm.types.stack.popi32(ctx^.ExecutionState.Operand_Stack), 10);
|
||||||
|
|
||||||
|
{ Test 2: else-body executes when condition is false }
|
||||||
|
{ i32.const 0 if $40 i32.const 10 else i32.const 20 end }
|
||||||
|
code[0] := $41; { i32.const 0 }
|
||||||
|
code[1] := $00;
|
||||||
|
code[2] := $04; { if }
|
||||||
|
code[3] := $40;
|
||||||
|
code[4] := $41; { i32.const 10 }
|
||||||
|
code[5] := $0A;
|
||||||
|
code[6] := $05; { else }
|
||||||
|
code[7] := $41; { i32.const 20 }
|
||||||
|
code[8] := $14;
|
||||||
|
code[9] := $0B; { end }
|
||||||
|
ctx := make_test_context(@code[0], 10);
|
||||||
|
while wasm.vm.tick(ctx) do;
|
||||||
|
assert_i32('false path executes else body', wasm.types.stack.popi32(ctx^.ExecutionState.Operand_Stack), 20);
|
||||||
|
|
||||||
|
{ Test 3: else skips nested block in else-body }
|
||||||
|
{ i32.const 1 if $40 i32.const 5 else block $40 i32.const 99 end end }
|
||||||
|
code[0] := $41; { i32.const 1 }
|
||||||
|
code[1] := $01;
|
||||||
|
code[2] := $04; { if }
|
||||||
|
code[3] := $40;
|
||||||
|
code[4] := $41; { i32.const 5 }
|
||||||
|
code[5] := $05;
|
||||||
|
code[6] := $05; { else }
|
||||||
|
code[7] := $02; { block }
|
||||||
|
code[8] := $40;
|
||||||
|
code[9] := $41; { i32.const 99 }
|
||||||
|
code[10] := $63;
|
||||||
|
code[11] := $0B; { end block }
|
||||||
|
code[12] := $0B; { end if }
|
||||||
|
ctx := make_test_context(@code[0], 13);
|
||||||
|
while wasm.vm.tick(ctx) do;
|
||||||
|
assert_i32('else skips nested block', wasm.types.stack.popi32(ctx^.ExecutionState.Operand_Stack), 5);
|
||||||
|
|
||||||
|
{ Test 4: execution continues after end when else is skipped }
|
||||||
|
{ i32.const 1 if $40 i32.const 3 else i32.const 4 end i32.const 7 }
|
||||||
|
code[0] := $41; { i32.const 1 }
|
||||||
|
code[1] := $01;
|
||||||
|
code[2] := $04; { if }
|
||||||
|
code[3] := $40;
|
||||||
|
code[4] := $41; { i32.const 3 }
|
||||||
|
code[5] := $03;
|
||||||
|
code[6] := $05; { else }
|
||||||
|
code[7] := $41; { i32.const 4 }
|
||||||
|
code[8] := $04;
|
||||||
|
code[9] := $0B; { end }
|
||||||
|
code[10] := $41; { i32.const 7 }
|
||||||
|
code[11] := $07;
|
||||||
|
ctx := make_test_context(@code[0], 12);
|
||||||
|
while wasm.vm.tick(ctx) do;
|
||||||
|
assert_i32('continues after else skip', wasm.types.stack.popi32(ctx^.ExecutionState.Operand_Stack), 7);
|
||||||
|
|
||||||
|
test_end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -34,6 +34,7 @@ uses
|
|||||||
wasm.test.opcode.block,
|
wasm.test.opcode.block,
|
||||||
wasm.test.opcode.loop,
|
wasm.test.opcode.loop,
|
||||||
wasm.test.opcode.ifop,
|
wasm.test.opcode.ifop,
|
||||||
|
wasm.test.opcode.elseop,
|
||||||
wasm.test.opcode.endop,
|
wasm.test.opcode.endop,
|
||||||
wasm.test.opcode.br,
|
wasm.test.opcode.br,
|
||||||
wasm.test.opcode.brif,
|
wasm.test.opcode.brif,
|
||||||
@@ -196,6 +197,7 @@ begin
|
|||||||
wasm.test.opcode.block.run;
|
wasm.test.opcode.block.run;
|
||||||
wasm.test.opcode.loop.run;
|
wasm.test.opcode.loop.run;
|
||||||
wasm.test.opcode.ifop.run;
|
wasm.test.opcode.ifop.run;
|
||||||
|
wasm.test.opcode.elseop.run;
|
||||||
wasm.test.opcode.endop.run;
|
wasm.test.opcode.endop.run;
|
||||||
wasm.test.opcode.br.run;
|
wasm.test.opcode.br.run;
|
||||||
wasm.test.opcode.brif.run;
|
wasm.test.opcode.brif.run;
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
unit wasm.vm.opcode.block;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BlockOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.constants, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BlockOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
end_ip: TWASMUInt32;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP); { past opcode $02 }
|
||||||
|
Inc(Context^.ExecutionState.IP); { past blocktype byte }
|
||||||
|
{ Find the matching end byte }
|
||||||
|
end_ip := scan_forward(Context^.ExecutionState.Code,
|
||||||
|
Context^.ExecutionState.IP,
|
||||||
|
Context^.ExecutionState.Limit, false);
|
||||||
|
{ Push block frame: br target = past the end byte }
|
||||||
|
push_control_frame(Context^.ExecutionState.Control_Stack,
|
||||||
|
CTRL_FRAME_BLOCK,
|
||||||
|
TWASMInt32(end_ip + 1),
|
||||||
|
TWASMInt32(Context^.ExecutionState.Operand_Stack^.Top));
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
unit wasm.vm.opcode.br;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses leb128, wasm.types.builtin, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
label_depth: TWASMUInt32;
|
||||||
|
bytesRead: TWASMUInt8;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP); { past opcode $0C }
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@label_depth);
|
||||||
|
{ do_branch sets IP; no need to advance past immediate }
|
||||||
|
do_branch(Context, label_depth);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
unit wasm.vm.opcode.brif;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrIfOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses leb128, wasm.types.builtin, wasm.types.stack, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrIfOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
label_depth: TWASMUInt32;
|
||||||
|
bytesRead: TWASMUInt8;
|
||||||
|
cond: TWASMInt32;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP); { past opcode $0D }
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@label_depth);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead); { past label depth }
|
||||||
|
cond := wasm.types.stack.popi32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if cond <> 0 then
|
||||||
|
do_branch(Context, label_depth);
|
||||||
|
{ else: continue at current IP }
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
unit wasm.vm.opcode.brtable;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrTableOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses leb128, wasm.types.builtin, wasm.types.stack, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_BrTableOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
count, idx, selected, dummy: TWASMUInt32;
|
||||||
|
skip_count, i: TWASMUInt32;
|
||||||
|
bytesRead: TWASMUInt8;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP); { past opcode $0E }
|
||||||
|
{ Read label count }
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@count);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead);
|
||||||
|
{ Pop index from operand stack }
|
||||||
|
idx := TWASMUInt32(wasm.types.stack.popi32(Context^.ExecutionState.Operand_Stack));
|
||||||
|
{ Determine how many labels to skip to reach our target }
|
||||||
|
if idx >= count then
|
||||||
|
skip_count := count
|
||||||
|
else
|
||||||
|
skip_count := idx;
|
||||||
|
{ Skip labels we don't need }
|
||||||
|
for i := 1 to skip_count do begin
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@dummy);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead);
|
||||||
|
end;
|
||||||
|
{ Read the selected label (or default if idx >= count) }
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@selected);
|
||||||
|
{ Branch to selected depth }
|
||||||
|
do_branch(Context, selected);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
unit wasm.vm.opcode.call;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_CallOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses leb128, lmemorymanager, wasm.types.builtin, wasm.types.enums,
|
||||||
|
wasm.types.values, wasm.types.sections, wasm.types.stack,
|
||||||
|
wasm.types.constants, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_CallOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
func_idx, type_idx: TWASMUInt32;
|
||||||
|
bytesRead: TWASMUInt8;
|
||||||
|
func_type: PWASMType;
|
||||||
|
code_entry: PWASMCodeEntry;
|
||||||
|
param_count, decl_count, total_count: TWASMUInt32;
|
||||||
|
new_locals: PWASMLocals;
|
||||||
|
i, j: TWASMUInt32;
|
||||||
|
return_ip, saved_top: TWASMUInt32;
|
||||||
|
cs, os: PWASMStack;
|
||||||
|
begin
|
||||||
|
cs := Context^.ExecutionState.Control_Stack;
|
||||||
|
os := Context^.ExecutionState.Operand_Stack;
|
||||||
|
|
||||||
|
Inc(Context^.ExecutionState.IP); { past opcode $10 }
|
||||||
|
bytesRead := read_leb128_to_uint32(
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.IP],
|
||||||
|
@Context^.ExecutionState.Code[Context^.ExecutionState.Limit],
|
||||||
|
@func_idx);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead);
|
||||||
|
return_ip := Context^.ExecutionState.IP; { instruction after call }
|
||||||
|
|
||||||
|
{ Look up function metadata }
|
||||||
|
type_idx := Context^.Sections.FunctionSection^.Functions[func_idx].Index;
|
||||||
|
func_type := @Context^.Sections.TypeSection^.Types[type_idx];
|
||||||
|
code_entry := @Context^.Sections.CodeSection^.Entries[func_idx];
|
||||||
|
|
||||||
|
param_count := func_type^.ParamCount;
|
||||||
|
decl_count := code_entry^.Locals.LocalCount;
|
||||||
|
total_count := param_count + decl_count;
|
||||||
|
|
||||||
|
{ Allocate new locals }
|
||||||
|
new_locals := PWASMLocals(kalloc(sizeof(TWASMLocals)));
|
||||||
|
new_locals^.LocalCount := total_count;
|
||||||
|
new_locals^.TypeCount := total_count;
|
||||||
|
if total_count > 0 then
|
||||||
|
new_locals^.Locals := PWASMValueEntry(kalloc(sizeof(TWASMValueEntry) * total_count))
|
||||||
|
else
|
||||||
|
new_locals^.Locals := nil;
|
||||||
|
|
||||||
|
{ Pop parameters from operand stack (reverse order into locals) }
|
||||||
|
if param_count > 0 then begin
|
||||||
|
for i := param_count downto 1 do begin
|
||||||
|
j := i - 1;
|
||||||
|
new_locals^.Locals[j].ValueType := func_type^.ParamTypes[j].ValueType;
|
||||||
|
case func_type^.ParamTypes[j].ValueType of
|
||||||
|
vti32: new_locals^.Locals[j].i32Value := wasm.types.stack.popi32(os);
|
||||||
|
vti64: new_locals^.Locals[j].i64Value := wasm.types.stack.popi64(os);
|
||||||
|
vtf32: new_locals^.Locals[j].f32Value := wasm.types.stack.popf32(os);
|
||||||
|
vtf64: new_locals^.Locals[j].f64Value := wasm.types.stack.popf64(os);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Initialize declared locals to zero }
|
||||||
|
if decl_count > 0 then begin
|
||||||
|
for i := param_count to total_count - 1 do begin
|
||||||
|
j := i - param_count;
|
||||||
|
new_locals^.Locals[i].ValueType := code_entry^.Locals.Locals[j].ValueType;
|
||||||
|
new_locals^.Locals[i].i64Value := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
saved_top := os^.Top; { stack depth after popping args }
|
||||||
|
|
||||||
|
{ Push call frame (4 entries): saved_locals_ptr, return_ip, saved_top, marker }
|
||||||
|
wasm.types.stack.pushi64(cs, TWASMInt64(Context^.ExecutionState.Locals));
|
||||||
|
wasm.types.stack.pushi32(cs, TWASMInt32(return_ip));
|
||||||
|
wasm.types.stack.pushi32(cs, TWASMInt32(saved_top));
|
||||||
|
wasm.types.stack.pushi32(cs, CTRL_FRAME_CALL);
|
||||||
|
|
||||||
|
{ Push implicit function block frame }
|
||||||
|
push_control_frame(cs, CTRL_FRAME_BLOCK,
|
||||||
|
TWASMInt32(code_entry^.CodeIndex + code_entry^.CodeLength),
|
||||||
|
TWASMInt32(os^.Top));
|
||||||
|
|
||||||
|
{ Switch to callee }
|
||||||
|
Context^.ExecutionState.IP := code_entry^.CodeIndex;
|
||||||
|
Context^.ExecutionState.Locals := new_locals;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
unit wasm.vm.opcode.callindirect;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_CallIndirectOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses console;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_CallIndirectOp(Context : PWASMProcessContext);
|
||||||
|
begin
|
||||||
|
console.writestringln('[wasm.vm.opcodes] Trap: call_indirect requires table support!');
|
||||||
|
Context^.ExecutionState.Running := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
unit wasm.vm.opcode.drop;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_DropOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses console;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_DropOp(Context : PWASMProcessContext);
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
if Context^.ExecutionState.Operand_Stack^.Top > 0 then
|
||||||
|
Dec(Context^.ExecutionState.Operand_Stack^.Top)
|
||||||
|
else begin
|
||||||
|
console.writestringln('[wasm.vm.opcodes.dropop] Stack underflow!');
|
||||||
|
Context^.ExecutionState.Running := false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
unit wasm.vm.opcode.elseop;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_ElseOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.values, wasm.types.stack, wasm.vm.control;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_ElseOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
cs, os: PWASMStack;
|
||||||
|
saved_top: TWASMInt32;
|
||||||
|
end_ip: TWASMUInt32;
|
||||||
|
result_entry: TWASMStackEntry;
|
||||||
|
begin
|
||||||
|
cs := Context^.ExecutionState.Control_Stack;
|
||||||
|
os := Context^.ExecutionState.Operand_Stack;
|
||||||
|
{ Reached by falling through from if-true body }
|
||||||
|
saved_top := cs^.Entries[cs^.Top - 2].i32Value;
|
||||||
|
{ Preserve result value if any }
|
||||||
|
if os^.Top > TWASMUInt32(saved_top) then begin
|
||||||
|
result_entry := os^.Entries[os^.Top - 1];
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
os^.Entries[os^.Top] := result_entry;
|
||||||
|
Inc(os^.Top);
|
||||||
|
end else begin
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
end;
|
||||||
|
{ Pop the IF frame }
|
||||||
|
Dec(cs^.Top, 3);
|
||||||
|
{ Skip the else body to the matching end }
|
||||||
|
Inc(Context^.ExecutionState.IP); { past else byte }
|
||||||
|
end_ip := scan_forward(Context^.ExecutionState.Code,
|
||||||
|
Context^.ExecutionState.IP,
|
||||||
|
Context^.ExecutionState.Limit, false);
|
||||||
|
Context^.ExecutionState.IP := end_ip + 1; { past end byte }
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
unit wasm.vm.opcode.endop;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_EndOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.values, wasm.types.constants;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_EndOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
cs, os: PWASMStack;
|
||||||
|
saved_top, return_ip: TWASMInt32;
|
||||||
|
result_entry: TWASMStackEntry;
|
||||||
|
begin
|
||||||
|
cs := Context^.ExecutionState.Control_Stack;
|
||||||
|
os := Context^.ExecutionState.Operand_Stack;
|
||||||
|
|
||||||
|
{ No control frame: end of top-level code }
|
||||||
|
if cs^.Top = 0 then begin
|
||||||
|
Context^.ExecutionState.Running := false;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Read the top control frame }
|
||||||
|
saved_top := cs^.Entries[cs^.Top - 2].i32Value;
|
||||||
|
|
||||||
|
{ Preserve block result value }
|
||||||
|
if os^.Top > TWASMUInt32(saved_top) then begin
|
||||||
|
result_entry := os^.Entries[os^.Top - 1];
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
os^.Entries[os^.Top] := result_entry;
|
||||||
|
Inc(os^.Top);
|
||||||
|
end else begin
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Pop the block frame }
|
||||||
|
Dec(cs^.Top, 3);
|
||||||
|
|
||||||
|
{ Check if a call frame is now on top (function return) }
|
||||||
|
if (cs^.Top >= 4) and (cs^.Entries[cs^.Top - 1].i32Value = CTRL_FRAME_CALL) then begin
|
||||||
|
saved_top := cs^.Entries[cs^.Top - 2].i32Value;
|
||||||
|
return_ip := cs^.Entries[cs^.Top - 3].i32Value;
|
||||||
|
{ Move return values to caller stack level }
|
||||||
|
if os^.Top > TWASMUInt32(saved_top) then begin
|
||||||
|
result_entry := os^.Entries[os^.Top - 1];
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
os^.Entries[os^.Top] := result_entry;
|
||||||
|
Inc(os^.Top);
|
||||||
|
end else begin
|
||||||
|
os^.Top := TWASMUInt32(saved_top);
|
||||||
|
end;
|
||||||
|
{ Restore locals }
|
||||||
|
Context^.ExecutionState.Locals := PWASMLocals(cs^.Entries[cs^.Top - 4].i64Value);
|
||||||
|
{ Pop call frame }
|
||||||
|
Dec(cs^.Top, 4);
|
||||||
|
{ Resume caller }
|
||||||
|
Context^.ExecutionState.IP := TWASMUInt32(return_ip);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Normal block/loop/if end: advance past end byte }
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
unit wasm.vm.opcode.f32const;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32ConstOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32ConstOp(Context : PWASMProcessContext);
|
||||||
|
var
|
||||||
|
value : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
value := TWASMPFloat(@Context^.ExecutionState.Code[Context^.ExecutionState.IP])^;
|
||||||
|
Inc(Context^.ExecutionState.IP, 4);
|
||||||
|
wasm.types.stack.pushf32(Context^.ExecutionState.Operand_Stack, value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32eq;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32EqOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32EqOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a = b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32ge;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32GeOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32GeOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a >= b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32gt;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32GtOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32GtOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a > b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32le;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LeOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LeOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a <= b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
unit wasm.vm.opcode.f32load;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LoadOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses console, leb128, wasm.types.builtin, wasm.types.heap, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LoadOp(Context : PWASMProcessContext);
|
||||||
|
var align_val, offset_val : TWASMUInt32; bytesRead : TWASMUInt8; addr : TWASMUInt32; result_val : TWASMUInt32;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
bytesRead := read_leb128_to_uint32(@Context^.ExecutionState.Code[Context^.ExecutionState.IP], TWASMPUInt8(Context^.ExecutionState.Code + Context^.ExecutionState.Limit), @align_val);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead);
|
||||||
|
bytesRead := read_leb128_to_uint32(@Context^.ExecutionState.Code[Context^.ExecutionState.IP], TWASMPUInt8(Context^.ExecutionState.Code + Context^.ExecutionState.Limit), @offset_val);
|
||||||
|
Inc(Context^.ExecutionState.IP, bytesRead);
|
||||||
|
addr := TWASMUInt32(wasm.types.stack.popi32(Context^.ExecutionState.Operand_Stack)) + offset_val;
|
||||||
|
if not wasm.types.heap.read_uint32(addr, Context^.ExecutionState.Memory, @result_val) then begin
|
||||||
|
console.writestringln('[wasm.vm.opcodes] Trap: f32.load out of bounds!');
|
||||||
|
Context^.ExecutionState.Running := false;
|
||||||
|
end else
|
||||||
|
wasm.types.stack.pushf32(Context^.ExecutionState.Operand_Stack, TWASMPFloat(@result_val)^);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32lt;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LtOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32LtOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a < b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
unit wasm.vm.opcode.f32ne;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses wasm.types.context;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32NeOp(Context : PWASMProcessContext);
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses wasm.types.builtin, wasm.types.stack;
|
||||||
|
|
||||||
|
procedure _WASM_opcode_F32NeOp(Context : PWASMProcessContext);
|
||||||
|
var a, b : TWASMFloat;
|
||||||
|
begin
|
||||||
|
Inc(Context^.ExecutionState.IP);
|
||||||
|
b := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
a := wasm.types.stack.popf32(Context^.ExecutionState.Operand_Stack);
|
||||||
|
if a <> b then
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 1)
|
||||||
|
else
|
||||||
|
wasm.types.stack.pushi32(Context^.ExecutionState.Operand_Stack, 0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
Loaded 20 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user