Description
According to the LLVM Language Reference:
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 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.