WebAssembly/Internals
Validation
There's a certain requirements to the binary code of WebAssembly.
The validation is described at the specification http://webassembly.github.io/spec/core/valid/index.html
A code that satisfies the validation rules, is considered to be well-formed
It's possible to generate a binary file with the code that doesn't satisfy the validation steps.
However, the browsers would not instantiate such binary file. Neither any other host should.
In some cases, there might be an overhead to create well-formed code.
Code Branching/Flow Control
todo: rewrite to the better explanation
WebAssembly doesn't allow direct jumps to (address/label)/or jump by offset. It only allows to jump "out-of" a code block, where the code block would be identified by a label. I.e. beginning of the loop label (for continue) or end of the loop label (for break). Loop itself is a single code block, known as "loop" in WebAssembly ).
FPC basic implementation is based of the ability of conditional jump instructions to a specified label. For that reason, the default implementation of TCGIfNode doesn't apply. Also, not every conditional jump instructions can have a label assign to it. Only instructions that jump out of the block are should use labels. Other, simply rely on the fact, that the next instruction IS the point they needs to be.
IF-block
Wasm IF do return a value (one of the basic WebAsm types). Pascal IF-statement does not return a value.
Thus the feature of value returning is not used (todo: but should be).
A dummy 0 value is pushed on the stack. IF is hard-coded to return i32 at all times.
According to the official documentation, IF always comes with "ELSE" block. However, some earlier versions do allow IF without an else statement. (which is also allowed in Binary specification of WASM). Such IF comes without any resulting type, and the execution of IF should leave stack unmodified in the end.
At this time "ELSE" branch is always generated for any IF statement. However, if there's no actual ELSE code (in pascal), a single "nop" instruction and a dummy result value produced.
function cmp(a,b: integer): Integer;
begin
if a > b then
cmp := 1
else
cmp := 0;
end;
turns into:
(func $cmp
(param i32)
(param i32)
(result i32)
(local i32) ;; Temp 2,1 allocated
(local i32) ;; Temp 3,1 allocated
;; [3] begin
;; [4] if a > b then
get_local 0
set_local 3
get_local 3
get_local 1
i32.gt_s
if (result i32) ;; hard-coded i32 type of IF result
;; [5] cmp := 1
i32.const 1
set_local 2
i32.const 0 ;; mandatory wasm-IF result
else
;; [7] cmp := 0;
i32.const 0
set_local 2
i32.const 0 ;; mandatory wasm-IF result
end
drop ;; dropping IF-result from the stack, as unused
;; [8] end;
get_local 2
return
)
The code generation of cifnode is overridden.
Loop-block
Loop is a block of instructions that has 2 flow labels. Begging (0) and end (1). All other block instructions only point to their "ends". Using branching br or br_if, it's possible to either leave or reiterate the loop.
Just like any other block, the loop has a result type. This is especially important when doing br_if, which requires the result value to be on the stack. (Verified by the validation process)
Here's an example if Loop. The function tries to sum-app a number X amount of times
function mulbyadd(Num, Cnt: integer): integer;
Result := 0;
while Cnt>0 do begin
dec(Cnt);
Result := Result + Num;
end;
end;
(func $mulbyadd (param $Num i32) (param $Cnt i32) (result i32)
(local i32) ;; local variable (that gets index #2) this is $Result
i32.const 0
set_local 2
loop $start
i32.const 0 ;; the resulting value of the loop, needed to keep well-formed Wasm
get_local $Cnt ;; comparing Cnt to 0
i32.const 0 ;;
i32.le_s
br_if 1 ;; if Cnt is less or equal to 0, then we should exit the loop
drop ;; dropping the result value of the loop from the stack
get_local $Cnt ;; decreasing Cnt
i32.const 1
i32.sub
set_local $Cnt
get_local $Num ;;
get_local 2 ;;
i32.add ;;
set_local 2 ;; Result := Result + Num
br 0 ;; loop back to the beginning
end $start
get_local 2
)