[我正在编写WebAssembly字节码分析器,并遇到了一些行为,各种WebAssembly编译器如何处理我很难与WebAssembly规范协调的循环指令。
以下代码段(摘自WebAssembly loop test cases)显示了一个嵌套循环,该循环有望返回i32整数。
(module
(type $t0 (func (result i32)))
(func $cont-inner (export "cont-inner") (type $t0) (result i32)
(local $l0 i32)
i32.const 0
local.set $l0
local.get $l0
loop $L0 (result i32)
loop $L1 (result i32)
br $L0
end
end
i32.add
local.set $l0
对上述内容的视觉分析表明,此语法严格不符合规范(据我所知),因为循环的主体没有要返回的任何堆栈值。
当然,以上循环形成一个无限循环,因此在执行时,程序实际上不会退出最外层循环。
但是我尝试过的几个编译器对此进行了编译,没有任何问题,例如webassembly.studio。相反,如果将无条件分支替换为条件分支,则编译器实际上会按照我的预期运行,并抱怨缺少返回值。
我是否错过了WebAssembly规范中有关循环如何运行的内容?还是编译器暗中进行了可达性分析?
您正在观察的是无条件分支之后的stack becoming polymorphic。这意味着堆栈的行为就像具有验证所需的值一样。
在这种情况下,br $L0
指令使堆栈多态。通常,从栈顶loop $L1
掉落会在堆栈上需要i32
,但是由于堆栈是多态的,因此类型检查器的行为就像是真的。
您可能会发现validation algorithm in the spec有用。我还写了关于WebAssembly type-checking a while back的文章,这可能对您有帮助。