gpt4 book ai didi

c# - 将 IntPtr 转换为 Int64 : conv. u8 或 conv.i8?

转载 作者:行者123 更新时间:2023-12-03 17:19:07 25 4
gpt4 key购买 nike

我正在处理 ILGenerator使用 Expression 帮助发射 IL 片段的扩展.一切都很好,直到我处理整数转换部分。有些东西对我来说确实违反直觉,例如:

  • 使用 conv.i8转换 Int32UInt64
  • 使用 conv.u8转换 UInt32Int64

  • 它们都是因为评估堆栈不跟踪整数符号。我完全理解原因,只是处理起来有点棘手。

    现在我想支持涉及 IntPtr 的转换.它必须更棘手,因为它的长度是可变的。我决定看看 C# 编译器是如何实现它的。

    现在专注于特定的 IntPtrInt64转换。显然,所需的行为应该是:在 64 位系统上无操作,或在 32 位系统上进行符号扩展。

    因为在 C# 中 native intIntPtr 包裹struct,我得看看它的主体 Int64 op_Explicit(IntPtr)方法。以下是 dnSpy 从 .NET core 3.1.1 反汇编的:
    .method public hidebysig specialname static 
    int64 op_Explicit (
    native int 'value'
    ) cil managed
    {
    .custom instance void System.Runtime.CompilerServices.IntrinsicAttribute::.ctor() = (
    01 00 00 00
    )
    .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = (
    01 00 00 00
    )
    .maxstack 8

    IL_0000: ldarga.s 'value'
    IL_0002: ldfld void* System.IntPtr::_value
    IL_0007: conv.u8
    IL_0008: ret
    }

    奇怪的是 conv.u8出现在这里!它将在 32 位系统上执行零扩展。我用以下代码确认了这一点:
    delegate long ConvPtrToInt64(void* ptr);
    var f = ILAsm<ConvPtrToInt64>(
    Ldarg, 0,
    Conv_U8,
    Ret
    );
    Console.WriteLine(f((void*)(-1))); // print 4294967295 on x86

    但是,在查看以下 C# 方法的 x86 指令时:
    static long Convert(IntPtr intp) => (long)intp;
    ;from SharpLab
    C.Convert(IntPtr)
    L0000: mov eax, ecx
    L0002: cdq
    L0003: ret

    事实证明,真正发生的是符号扩展!

    我注意到 Int64 op_Explicit(IntPtr)有一个 Intrinsic属性。是不是方法体被运行时 JIT 完全忽略了,而是被一些内部实现取代了?

    最后一个问题:我必须引用 IntPtr的转换方法吗?实现我的转换?

    附录 我的 ILAsm执行:
    static T ILAsm<T>(params object[] insts) where T : Delegate =>
    ILAsm<T>(Array.Empty<(Type, string)>(), insts);

    static T ILAsm<T>((Type type, string name)[] locals, params object[] insts) where T : Delegate
    {
    var delegateType = typeof(T);
    var mi = delegateType.GetMethod("Invoke");
    Type[] paramTypes = mi.GetParameters().Select(p => p.ParameterType).ToArray();
    Type returnType = mi.ReturnType;

    var dm = new DynamicMethod("", returnType, paramTypes);
    var ilg = dm.GetILGenerator();

    var localDict = locals.Select(tup => (name: tup.name, local: ilg.DeclareLocal(tup.type)))
    .ToDictionary(tup => tup.name, tup => tup.local);

    var labelDict = new Dictionary<string, Label>();
    Label GetLabel(string name)
    {
    if (!labelDict.TryGetValue(name, out var label))
    {
    label = ilg.DefineLabel();
    labelDict.Add(name, label);
    }
    return label;
    }

    for (int i = 0; i < insts.Length; ++i)
    {
    if (insts[i] is OpCode op)
    {
    if (op.OperandType == InlineNone)
    {
    ilg.Emit(op);
    continue;
    }
    var operand = insts[++i];
    if (op.OperandType == InlineBrTarget || op.OperandType == ShortInlineBrTarget)
    ilg.Emit(op, GetLabel((string)operand));
    else if (operand is string && (op.OperandType == InlineVar || op.OperandType == ShortInlineVar))
    ilg.Emit(op, localDict[(string)operand]);
    else
    ilg.Emit(op, (dynamic)operand);
    }
    else if (insts[i] is string labelName)
    ilg.MarkLabel(GetLabel(labelName));
    else
    throw new ArgumentException();
    }
    return (T)dm.CreateDelegate(delegateType);
    }

    最佳答案

    我犯了一个错误。 Int64 op_Explicit(IntPtr)有两个版本。
    64位版本位于“C:\Program Files\dotnet...”,其实现为:

    .method public hidebysig specialname static 
    int64 op_Explicit (
    native int 'value'
    ) cil managed
    {
    .maxstack 8

    IL_0000: ldarga.s 'value'
    IL_0002: ldfld void* System.IntPtr::_value
    IL_0007: conv.u8
    IL_0008: ret
    }

    32位版本位于“C:\Program Files (x86)\dotnet...”,其实现为:
    .method public hidebysig specialname static 
    int64 op_Explicit (
    native int 'value'
    ) cil managed
    {
    .maxstack 8

    IL_0000: ldarga.s 'value'
    IL_0002: ldfld void* System.IntPtr::_value
    IL_0007: conv.i4
    IL_0008: conv.i8
    IL_0009: ret
    }

    谜题解决了!

    不过,我认为可以在 32 位和 64 位构建中使用一种相同的实现。一 conv.i8将在这里完成工作。

    事实上,我可以简化发射 IntPtr 的任务。转换,因为在运行时,'IntPtr' 的长度是已知的(据我所知是 32 或 64),并且大多数发出的方法不会被保存和重用。但我仍然想要一个独立于运行时的解决方案,我想我已经找到了。

    关于c# - 将 IntPtr 转换为 Int64 : conv. u8 或 conv.i8?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60881564/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com