<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/63755>63755</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[WebAssembly] zero-length memcpy does not result in no-op when bulk-memory is enabled
</td>
</tr>
<tr>
<th>Labels</th>
<td>
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
Luukdegram
</td>
</tr>
</table>
<pre>
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.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysV09v2z4S_TT0ZWBBoixbPviQNDU2QBYLFIvtsaDIkcQtRQokZdf99D-QkhM5cYw0KGDI-sN5M_NmSD4y52SjEXekuCfFw4INvjV29zQMPwU2lnWLyojT7o5zY4XUDXgDvkV4evrfv4EU909MNwNrEL5hjRY1R1I8EFq23veO5HeE7gndK3XoEmMbQvfCcEfoPth9wzppfacIzaUo0oLQbbBIH0h6R_Kv8FgDyb8o1OFBOkgJ_QLSh1ttlqaHzohBjfFU2LKDNBZMDcx7K6vBowu3jLcozmEz2wwdau-SN-jaeGBwRKWWAmupUcCBqQGD0wsP0sGgpyHXYX6jNcGsMr4NXwU6Hz4zLcKjszw8udYMSkCFF06DnfEt2qN0eMPxRFO89mhrY7tQHgYddsaegJv-BEfpW2AxnqVC3fgWLLpBeQdSA5vTiAn8t5UOOmTawSOhGwH4q0fuoTY2BvIdqzvnsKvUCTyzDfrRQfhG1mk1qJ_L0TtZp1Aj84NFQM0qNeblzeR-7v3Yoo4QU4Ch0An8yxzxgHYkXzpwiJ0LANjJUCeyTkdXSUg0-JPaeTtwL40ew9IGeIv8pwsgIa3Aj_TAmb4Iw1s2iyLUSmoWYZgQFp0LIZnBL029rMyghbsgXyaYQCMPk31tlDLH4Orx20s3r9PpNzX3PewHPcZ6570NMwW0sSh-G42gzaCPUotx9FhxEM78UIYzBQcjBZBV-sN5Zj2hJaFbIDRPgdBMVE34o8UayOZ-RPiqvT09BwNAaJECyR-AqYAYBkLvI9kyp0A2D-GWKdloWM2Mss8Y0WjEmVJQM-c5v2IZsqmNSWpjxnTCt1ky62ICdN5YvApACxqee29jpPNY3sEaYzqTGZcoUTWJQK6YRULLDj0TzLML0OeXEYu-fvXw-PVXH5pGGn1O5d1sAmj-N-hZnwFv8ZPP-Elv8LP-LD_pW35Wn-JnPeNnFflRhol3Ou4DGW1meMUH8W510BxvHfHwl7eM-7hhvEN_EZ7TW1Cbj0LFgLJbUOUfQb0f1dsW6LDj_Snp0_CTOSW0DIyNTGXR95nF2bv1s1tabOJ9BjVTDq9U_9m5RR99vzMg1Gu2Dn9qUZXao9XsT-YeDFqzDsWPsDu8WXbX5ceX3cjemyVznMLB-zL7WH9vVn9vxm4-taK9RBBqdo3E8VXg25vReTmmGPb0OHh7Hp-eZ-LcQXG95q_21Xj9LpX6mExgyrdmaNq5_DB1fDI92iDXRjXyqEcVwpl71oMfEQrTWIt1qOkoOqIKOeuOBN4oa1Lcz4WW65HLWvLo6Jq2bqRvhyrhpiN0P7MkdD9TZMuYUMAI6rtSpiJ03zHng8ba99b0xjHlbhn954D2IPGYdILQfBwRWF3OKA3zg-nTJQtnSfqMBsdQotcaLJBlsWFWqMDlVIiacQ9HhJYd8JWUnWo0CddJe18o5gsZy66eYd4eYCZ1F0FfjhVancBwPlj3ohXfF73SnXUvMBflqBn82EPTkJDtEUEEzjZ-So_zkPn5rFKZA0I4rUgTTiMXnavFGP5x1uzKmD6OwrC50fJ7K3nU047VGFX8nD1ueomO0O2Fmp2odAMCU86cUz4fAcj6eRdw6Mf55K3UTvILmPG6ELtcbPMtW-AuW5dlSYvNqly0O1qxbMO3q6zORVplWcm2vC6ySpRYl_mmXMgdTWmebtIyW2XFqkgy3BarqspWNMuqOi_JKsWOSZWcj5aLGPRunW-KYqFYhcqdz7R2FwYtq6FxYVmUzrsXMy-9iqff-dQpHi6oGvc8EAbHHnvp3NkRZtYJs_IvBqt2NyZsiGP6W_bW_B-5J3QfcwlzLqbzTwAAAP__3CzMWA">