Issue 63755
Summary [WebAssembly] zero-length memcpy does not result in no-op when bulk-memory is enabled
Labels
Assignees
Reporter Luukdegram
    According to the LLVM [Language Reference](https://llvm.org/docs/LangRef.html#id505):
> 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.



_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to