After stumbling about Unsafe.CopyBlock() and Unsafe.CopyBlockUnalinged() a second time I finally decided to take a look at the source of System.Runtime.CompilerServices.Unsafe.
Comparing the IL of CopyBlock()
.method public hidebysig static void CopyBlock(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
cpblk
ret
}
with the code of CopyBlockUnaligned()
.method public hidebysig static void CopyBlockUnaligned(void* destination, void* source, uint32 byteCount) cil managed aggressiveinlining
{
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 3
ldarg.0
ldarg.1
ldarg.2
unaligned. 0x1
cpblk
ret
}
We can see that the only noticable difference is the unaligned. 0x1 right before the call to cpblk in the Unsafe.CopyBlockUnaligned() implementation. As I'm not too fluent in IL taking a look at the documentation of System.Reflection.Emit.OpCodes.Unaligned reveals the definition of the unaligned keyword:
Indicates that an address currently atop the evaluation stack might not be aligned to the natural size of the immediately following ldind, stind, ldfld, stfld, ldobj, stobj, initblk, or cpblk instruction.
and further down it states
Unaligned specifies that the address (an unmanaged pointer, native int) on the stack might not be aligned to the natural size of the immediately following ldind, stind, ldfld, stfld, ldobj, stobj, initblk, or cpblk instruction. That is, for a Ldind_I4 instruction the alignment of the address may not be to a 4-byte boundary. For initblk and cpblk the default alignment is architecture dependent (4-byte on 32-bit CPUs, 8-byte on 64-bit CPUs).
Therefore it is safe to say that Unsafe.CopyBlock() should only be used when we know that both source and destination addresses are aligned which currently only inofficially includes addresses returned by Marshal.AllocHGLobal(). However the upcoming .NET 6 (currently in preview) will bring us NativeMemory to control memory alignment ourselves which would allow us to use Unsafe.CopyBlock() more freely. So if unsure about the alignment just using Unsafe.CopyBlockUnaligned() is recommended.