The clang disassembly given in the article sure makes it look like WASM is a nested expression tree, which leaves the choice of stack versus register to the implementation.
(f32.add
(f32.add
(f32.mul
(f32.load
(local.get 0))
(f32.load
(local.get 1)))
(f32.mul
(f32.load offset=4
(local.get 0))
(f32.load offset=4
(local.get 1))))
(f32.mul
(f32.load offset=8
(local.get 0))
(f32.load offset=8
(local.get 1))))
The outer f32.add could translate into a byte code instruction that finds its two operands on a stack, or to one which gets them from registers.The code only says that there is a f32.add call which has two operands that are the result of a f32.add and f32.mul and so on.
The implementations will agree in their treatment of locals: that there are two locals 0 and 1, which support loading at offsets and such.
Both stack and register machines can support locals.