【问题标题】:Jitter logic to remove unbox_any移除 unbox_any 的抖动逻辑
【发布时间】:2016-03-26 18:14:51
【问题描述】:

我正在调查这段 C# 代码的执行情况:

public static void Test<T>(object o) where T : class
{
    T t = o as T;
}

等效的IL代码为:

.method public static void  Test<class T>(object A_0) cil managed
{
  // Code size       13 (0xd)
  .maxstack  1
  .locals init (!!T V_0)
  IL_0000:  ldarg.0
  IL_0001:  isinst     !!T
  IL_0006:  unbox.any  !!T
  IL_000b:  stloc.0
  IL_000c:  ret
} // end of method DemoType::Test

根据这个答案 (unnecessary unbox_any),谁能向我解释一下 Jitter 在这里做什么的确切逻辑;在这种特定情况下,Jitter 究竟是如何决定忽略 'unbox_any' 指令的(理论上,根据msdn,当 isinst 指令产生 null 时应该抛出 NullReferenceException,但实际上不会发生这种情况!)

更新

根据 usr 的回答和 Hans 的评论,如果 obj 是引用类型,则将调用 castclass,因此不会调用 NRE。

但是下面的情况呢?

static void Test<T>(object o) where T : new()
    {
        var nullable = o as int?;
        if (nullable != null)
            //do something
    }

Test<int?>(null);

以及等效的IL代码(部分):

IL_0001:  ldarg.0
IL_0002:  isinst     valuetype [mscorlib]System.Nullable`1<int32>
IL_0007:  unbox.any  valuetype [mscorlib]System.Nullable`1<int32>
IL_000c:  stloc.0
IL_000d:  ldloca.s   nullable
IL_000f:  call       instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_0014:  stloc.1
IL_0015:  ldloc.1
IL_0016:  brfalse.s  IL_0024

在这种情况下它的值类型为什么不抛出 NRE?

【问题讨论】:

  • public static void Test&lt;T&gt;(T o) where T : class 因为从未抛出运行时异常。
  • @HamletHakobyan 你在实践中是对的。但它应该根据 msdn(请参阅 msdn 链接)。没有抛出异常的原因是因为抖动发出 unbox_any 指令(参见其他答案链接)。
  • "当应用于引用类型时,unbox.any 指令与 castclass typeTok 具有相同的效果。如果操作数 typeTok 是泛型类型参数,则运行时行为是由为该泛型类型参数指定的类型确定。” 在文档中的哪个位置指定了 NRE?
  • @YuvalItzchakov 我看到了,但我想确定确切的逻辑。那么,类型的抖动检查,如果它是引用类型,unbox_any 会被忽略吗?
  • 如果您需要证明,只需 look at the source。注意它在!eeIsValueClass(resolvedToken.hClass)上的快捷方式

标签: c# .net optimization clr jitter


【解决方案1】:

当应用于引用类型时,unbox.any 指令与 castclass typeTok 具有相同的效果。

T 被限制为引用类型。在这种情况下,该指令不会引发 NRE。 JIT 不会“忽略”它,它会按规定执行它。 JIT 不允许忽略指令。

文档有说明

如果 obj 为空引用,则抛出 NullReferenceException。

这是一种误导,因为它只适用于值类型。我引用的第一句话是明确的。

【讨论】:

  • 在这种特定情况下,isinst 指令返回 null,因此 unbox_any 应该抛出 NRE。 “如果 obj 是空引用,则抛出 NullReferenceException。” 因此,如果抖动没有“忽略”它,那么如果您将运行此代码,则应该获得 NRE。
  • 我已经解释了为什么不是这样。如果您不同意,请解释原因。不要只是声称答案是错误的。这阻碍了我们取得进展。
  • 因此,如果它是值类型,则 obj 必须为非 null 否则将抛出 NRE,如果这是引用类型,则会发生 castclass 逻辑。这是有道理的,但如果它是正确的,如果我们只是在 isinst 中做这样的事情并且我们已经得到了 null,为什么还要发生 castclass 逻辑呢?我不是说我不同意,我只是问问。
  • 在这里谈论 val 类型没有实际意义,因为 T 被限制为 ref 类型。如果您删除 where : class 约束,您将无法再使用 as(编译器错误)。
  • 我想我在源代码中找到了答案。我还没有弄清楚。谢谢。
【解决方案2】:

要回答可空值类型的第二种情况(问题的更新部分),我们需要仔细查看 ECMA CLI 规范(III.4.33 unbox.any - 将装箱类型转换为值):

如果 obj 为 null 并且 typeTok 是不可为 null 的值类型,则会引发 System.NullReferenceException

MSDN 文档中缺少粗体部分。

所以总结一下unbox_any的行为:

  1. 如果 typeTok 是 ref 类型,则行为与 castclass 相同
  2. 如果 typeTok 是值类型:

    2.1。如果 obj 为 null 且 typeTok 为可空值类型,则结果为 null

    2.2。如果 obj 为 null 且 typeTok 不是可空值类型,则会抛出 NullReferenceException

如果我理解正确,第 2.2 段的行为与常规拆箱操作相同see jitter source code

【讨论】:

    猜你喜欢
    • 2018-07-04
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 2015-01-16
    • 2020-07-10
    • 2011-02-09
    • 2019-12-11
    • 2020-07-22
    相关资源
    最近更新 更多