Search code examples
javascriptcclangllvmwebassembly

Does Clang provide intrinsics for WebAssembly's memory.fill and memory.copy?


I'm working on developing a WebAssembly module in C and have been attempting to utilize the memory.fill and memory.copy instructions defined in the WebAssembly spec.

I know that Clang (v11.1.0) already supports other memory-related wasm intrinsics like __builtin_wasm_memory_size and __builtin_wasm_memory_grow, but I've been having a hard time working out if it supports intrinsics for memory.fill and memory.copy.

I'm not intimately familiar with the inner workings of Clang/LLVM but references to these instructions seem to be hinted at in a few places [1] [2] (unless I'm misinterpreting something).

I attempted to use __builtin_memcpy and __builtin_memset:

__attribute__((export_name("memcpy")))
void memcpy_test(void* mem_dst, void* mem_src) {
    __builtin_memcpy(mem_dst, mem_src, 128);
}

__attribute__((export_name("memset")))
void memset_test(void* mem_src) {
    __builtin_memset(mem_src, 0, 128);
}

which compiles, but the calls to __builtin_memcpy and __builtin_memset are replaced with imported functions in the assembly:

...
  (import "env" "memcpy" (func $memcpy (type $t0)))
  (import "env" "memset" (func $memset (type $t0)))
  (func $__wasm_call_ctors (type $t1))
  (func $memcpy_test (type $t2) (param $p0 i32) (param $p1 i32)
    local.get $p0
    local.get $p1
    i32.const 128
    call $memcpy
    drop)
  (func $memset_test (type $t3) (param $p0 i32)
    local.get $p0
    i32.const 0
    i32.const 128
    call $memset
    drop)
...

At this point I'm a little stuck, it seems like these intrinsics aren’t available yet, but I’m not positive of that.

I'd really appreciate any help I could get!

[Quick note: I'm not currently interested in using emscripten as I'm trying to avoid the large amount of glue code it comes with.]


Solution

  • but the calls to __builtin_memcpy and __builtin_memset are replaced with imported functions in the assembly:

    The reason is that those instructions are not part of the core (MVP) WebAssembly instruction set, and were added later in the bulk-memory proposal.

    Those later proposals need to be opted in explicitly, since there is a risk that a given WebAssembly engine you might be targeting is not supporting them yet. To be sure, you can check my support table on webassembly.org.

    Finally, if you already checked this, you can opt in to this feature in Clang via -mbulk-memory flag. For example, with this flag and optimisations enabled

    clang temp.c -mbulk-memory -O -c -o temp.wasm
    

    this is what I get for your sample above:

    (module
      (type $t0 (func (param i32 i32)))
      (type $t1 (func (param i32)))
      (import "env" "__linear_memory" (memory $env.__linear_memory 0))
      (import "env" "__indirect_function_table" (table $env.__indirect_function_table 0 funcref))
      (func $memcpy_test (type $t0) (param $p0 i32) (param $p1 i32)
        (memory.copy
          (local.get $p0)
          (local.get $p1)
          (i32.const 128)))
      (func $memset_test (type $t1) (param $p0 i32)
        (memory.fill
          (local.get $p0)
          (i32.const 0)
          (i32.const 128)))
      (export "memcpy" (func $memcpy_test))
      (export "memset" (func $memset_test)))
    

    which seems to be the expected result.