Why does clang emit i64 instructions when targeting wasm32?

Viewed 173

I used clang --target=wasm32-unknown-wasi ... to compile a Hello World C program into WebAssembly. Everything goes fine, and the .wasm file can run correctly in runtime like wasmer.

But when I check the .wasm file(in .wat format), I found some i64 instructions like:

(func $__lseek (type $t1) (param $p0 i32) (param $p1 i64) (param $p2 i32) (result i64)
    (local $l3 i32)
    global.get $g0
    i32.const 16
    i32.sub
    local.tee $l3
    global.set $g0
    block $B0
      block $B1
        local.get $p0
        local.get $p1
        local.get $p2
        i32.const 255
        i32.and
        local.get $l3
        i32.const 8
        i32.add
        call $__wasi_fd_seek
        local.tee $p0
        i32.eqz
        br_if $B1
        i32.const 0
        i32.const 70
        local.get $p0
        local.get $p0
        i32.const 76
        i32.eq
        select
        i32.store offset=3696
        i64.const -1
        local.set $p1
        br $B0
      end
      local.get $l3
      i64.load offset=8
      local.set $p1
    end
    local.get $l3
    i32.const 16
    i32.add
    global.set $g0
    local.get $p1)

It looks strange: why would clang emit i64 instructions when targeting wasm32?

According to the discussions in https://github.com/WebAssembly/wasi-libc/issues/158 , I think it's probably because some wasi APIs which includes i64 instructions were introduced during link time. Am I right?

Thanks for your attention!

1 Answers

I see nothing wrong with that. Targeting 32bits means that the memory addressing is 32bits. For this reason before the memory store instruction, all you have are 32bit instructions (i.e. the offset and the value are i32) you have this at some point:

i32.store offset=3696

That is the storing in the implicit memory index 0 (the only one available today). The value that is stored is integer 32bit, at offset i32 (computed before that + 3696). The offset is what you care about in this case, to be i32, and it is. First the offset is computed, then the value.

The MVP of WebAssembly supports 64bit integers "out of the box". That means that you can use such values freely, as well as f32 and f64. That however are the values in your program, the MVP only supports i32 memory addressing. Just recently BitInt became the JS type that matches to i64 in WebAssembly, and the memory64 post-MVP is preparing to enter the production systems.

In details you have this:

(; push 0 (i32 type) ;)
i32.const 0
(; stack depth from here = 1 ;)

(; push 70 (i32 type) ;)
i32.const 70
(; stack depth from here = 2 ;)

(; push the first parameter two times (that is of i32 type) ;)
local.get $p0
local.get $p0
(; stack depth from here = 4 ;)

(; push 76 (i32 type))
i32.const 76
(; stack depth from here = 5 ;)

(; check is 76 equal to $p0, pop 2, push 1 all i32 types ;) 
i32.eq
(; stack depth from here = 4 ;)

(; [t t i32] -> [t], i.e. pop 3, push 1, all i32 types ;)
select
(; stack depth from here = 2 ;)

(; [offset i32, value i32] -> [], pop 2, push zero ;)
i32.store offset=3696

(; stack depth 0 ;)

The immediate offset=3696 is an offset that will be added to the computed offset from the stack to receive the effective address where the value will be written exactly.

Related