Full semantics of the Cpblk opcode in MSIL

The MSDN documentation is for a cpblk

bit sparse:

The command cpblk

copies the number (type unsigned int32

) of bytes from the source address (type *

, native int

or &

) to the destination address (type *

, native int

or &

). The behavior is cpblk

not specified if the source and destination areas overlap.

cpblk

assumes that both the source and destination addresses match the natural size of the machine. The command cpblk

can be preceded by an instruction unaligned.

to indicate that the source or destination is not bound.

Okay, compared to other bulk copy operations like Array.Copy

, Marshal.Copy

and Buffer.BlockCopy

, we know that:

  • Size is measured in bytes
  • Pointers must be aligned

This leaves me with a few questions:

  • Do I need to buffer the buffers first? Does the type of the operand matter native int

    , "unmanaged pointer" or "managed pointer ( &

    )"?
  • Are there any restrictions on the type? (for example Buffer.BlockCopy

    only works with primitive types, not structures, even if they only contain primitive types)

According to this StackOverflow question , no pinning is needed, but the supporting explanation is simply wrong. (I suspect this is an over-generalization due to the LOB bunch not being compact)

ECMA-335 doesn't help much either. The description of the instruction contains the same wording and adds

[Rationale: cpblk

intended to copy structures (not arbitrary bytes). All such CLI-allocated structures naturally align for the current platform. Therefore, the compiler does not need to generate instructions cpblk

to know if the code will eventually execute on a 32-bit or 64-bit platform. ultimate justification]

Ok, it looks like it should accept more types than Buffer.BlockCopy

. But still not arbitrary types.

Perhaps the recently released .NET source code will contain some answers.

+3


source to share


1 answer


cpblk

and its companion,, initblk

maps directly to internals that any native language compiler depends on initializing and copying structures. No need to wait for .NETCore source, you can see their semantics from SSCLI20, clr / src / fjit / fjitdef.h. Simple jitter converts cpblk

directly into a call memcpy()

, initblk

up to memset()

. The same features that the C compiler uses.

Of course, disregarding GC, the C # and VB.NET compilers don't use these opcodes at all. But the C ++ / CLI compiler does, a simple example:

using namespace System;

struct s { int a; int b;  };

int main(array<System::String ^> ^args)
{
    s var = {};        // initblk
    s cpy = var;       // cpblk
    return 0;
}

      



Optimized MSIL:

.method assembly static int32  main(string[] args) cil managed
{
  // Code size       34 (0x22)
  .maxstack  3
  .locals ([0] valuetype s cpy,
           [1] valuetype s var)
  IL_0000:  ldloca.s   var
  IL_0002:  ldc.i4.0
  IL_0003:  ldc.i4.8
  IL_0004:  initblk
  IL_0006:  ldloca.s   cpy
  IL_0008:  ldloca.s   var
  IL_000a:  ldc.i4.8
  IL_000b:  cpblk
  ...
}

      

.NET current tags generate inline code with simple register moves for small structures, REP STOS / MOVS for large ones. Very similar to what Buffer.Memcpy () does.

+7


source







All Articles