According to the LLVM [Language Reference](https://llvm.org/docs/LangRef.html#id…505):
> If <len> is 0, it is no-op modulo the behavior of attributes attached to the arguments. If <len> is not a well-defined value, the behavior is undefined. If <len> is not zero, both <dest> and <src> should be well-defined, otherwise the behavior is undefined.
performing a memory copy with a zero-length results in a no-op module. This means I'd expect for the WebAssembly target with the `bulk-memory` feature enabled, to result in a no-op when the length is 0. However, this seems to emit a `memory.copy` instruction with no checks, meaning it can result in a trap when the destination address is out-of-bounds.
i.e. given the following IR:
```
; Function Attrs: noredzone nounwind
define dso_local void @_start() #0 !dbg !256 {
Entry:
%0 = alloca { ptr, i32 }, align 4
%1 = alloca { ptr, i32 }, align 4
%2 = call fastcc { ptr, i32 } @foo.foo(), !dbg !265
store { ptr, i32 } %2, ptr %1, align 4, !dbg !265
call void @llvm.dbg.declare(metadata ptr %1, metadata !262, metadata !DIExpression()), !dbg !265
%3 = call fastcc { ptr, i32 } @foo.foo(), !dbg !266
store { ptr, i32 } %3, ptr %0, align 4, !dbg !266
call void @llvm.dbg.declare(metadata ptr %0, metadata !264, metadata !DIExpression()), !dbg !266
%4 = load { ptr, i32 }, ptr %0, align 4, !dbg !267
%5 = load { ptr, i32 }, ptr %1, align 4, !dbg !267
%6 = extractvalue { ptr, i32 } %5, 0, !dbg !267
%7 = extractvalue { ptr, i32 } %4, 1, !dbg !267
%8 = extractvalue { ptr, i32 } %4, 0, !dbg !267
call void @llvm.memcpy.p0.p0.i32(ptr align 1 %8, ptr align 1 %6, i32 %7, i1 false), !dbg !267
ret void, !dbg !267
}
; Function Attrs: noredzone nounwind
define internal fastcc { ptr, i32 } @foo.foo() unnamed_addr #0 !dbg !268 {
Entry:
%0 = alloca i32, align 4
store i32 -1, ptr %0, align 4, !dbg !274
call void @llvm.dbg.declare(metadata ptr %0, metadata !272, metadata !DIExpression()), !dbg !274
ret { ptr, i32 } { ptr inttoptr (i32 -1 to ptr), i32 0 }, !dbg !275
}
```
Will emit a `memory.copy` instruction although the length of the operand is 0. In this case, the destination address is out-of-bounds, therefore resulting in a trap. According to the [WebAssembly specification](https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md#memorycopy-instruction) any out-of-bounds memory operation will result in a trap, regardless of the fact we have a zero-length operand. This is not the behavior I'd expect according to the LLVM Language Reference.
This behavior only occurs when the `bulk-memory` feature is enabled as without this feature, we don't have access to the above mentioned instruction and LLVM will emit a loop instead (Which is safe for zero-length copies).
This issue also occurs for the `llvm.memset` intrinsic.