什么被认为是OpCodes.Ldobj的“自然对齐”?

时间:2015-06-23 20:26:44

标签: c# alignment cil opcode

我正在使用“Reflection.Emit”,并希望生成一个静态类,在C#中公开带有此签名的方法:

unsafe static void CopyStruct<T>(void * dest, ref T src) where T : struct
{
    // copy struct from src to dest using ldobj/stobj
}

这是为了能够非常快速(并且不安全地)将blittable结构复制到非托管内存。但是,我遇到了一些关于 OpCodes.Ldobj alignment 的问题。

使用以下两种结构和方法:

struct Def { byte fill0; Int64 fill1; }            // size = 16
static void CopyDef(ref Def dest, ref Def src) { dest = src; }

[StructLayout(LayoutKind.Sequential, Pack = 1)]    // size = 9
struct Seq { byte fill0; Int64 fill1; }
static void CopySeq(ref Seq dest, ref Seq src) { dest = src; }

我希望这些方法可以生成如下内容:

{ // CopyDef
  ldarg.0
  ldarg.1
  ldobj      Def
  stobj      Def
  ret
}
{ // CopySeq
  ldarg.0
  ldarg.1
  unaligned. 1
  ldobj      Seq
  unaligned. 1
  stobj      Seq
  ret
}

相反,我得到的 Seq 的输出与 Def 的输出相同,即在任何地方都没有未对齐的x 操作码。我期望ldobj / stobj将除了自然机器对齐之外的任何东西(例如8/4)视为未对齐,但我显然是错误的。

我的问题是后续跟进:

由于ldobj / stobj操作码必须指定它们正在操作的地址类型,因此“Pack = 1”会以某种方式隐式“处理”吗?

如果是这种情况,这是否意味着具有“Pack = 4”的结构被隐含地“照顾”,只要该地址不引用低于4的偏移(例如2,1),其中如果操作码必须以未对齐x 开头?当然,这只会出现在初始方法签名中的非托管指针。

更新

来自 OpCodes.Ldind_I4 MSDN(1):

  

所有ldind指令都是Ldobj指令的快捷方式,指定相应的内置值类。

来自 OpCodes.Unaligned MSDN(2):

  

未对齐指定堆栈上的地址...可能不会与紧随其后的ldind,stind,... ldobj,stobj ...指令的自然大小对齐。也就是说,对于Ldind_I4指令,地址的对齐可能不是4字节边界。

来自 OpCodes.Ldobj MSDN(3):

  

复制的字节数取决于类的大小(由class参数指定)。 class参数是表示值类型的元数据标记。

来自 OpCodes.Ldind_I4 MSDN(4):

  

返回地址的所有 MSIL指令的结果(例如,Ldloca和Ldarga)安全对齐。

建议

  1. “Ldind_I4”==“Ldobj Int32”
  2. 4字节 int上的Ldobj认为 4字节偏移量要对齐。
  3. Ldobj“意识到”它所操作类型的大小。
  4. Ldelema无法生成未对齐的地址。
  5. 任何blittable结构的数组中最差(最低)可能的偏移量是:

    int offset = sizeof(myStruct) & 7; // 3 for 32bit
    // offset == 0     -> correct
    // offset == 1,3,7 -> unaligned. 1
    // offset == 2,6   -> unaligned. 2
    // offset == 4     -> unaligned. 4
    

    Seq 的大小为9,它给出了7的偏移量,这意味着我们(最坏的情况)未对齐1。

    以下CopySeq方法不会生成未对齐的操作码

    static void CopySeq(ref Seq dest, Seq[] src) { dest = src[1]; }
    
    { // CopySeq
        ldarg.0
        ldarg.1
        ldc.i4.1
        ldelema    Seq
        ldobj      Seq
        stobj      Seq
        ret
    }
    

    除非ldelema对后续的ldobj做了一些魔术,否则ldobj 应该知道如何处理在指定类型的数组中可能发生的任何错位,这可以通过大小来确定那种类型。

0 个答案:

没有答案
相关问题