C# 编译器 +带拳击的通用代码 +限制条件

发布于 2024-08-09 21:27:15 字数 811 浏览 2 评论 0原文

让我们检查一下为以下泛型方法生成的 MSIL 代码:

public static U BoxValue<T, U>(T value)
  where T : struct, U
  where U : class
{
  return value;
}

看:

.method public hidebysig static !!U  BoxValue<valuetype .ctor
 ([mscorlib]System.ValueType, !!U) T,class U>(!!T 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  unbox.any  !!U
  IL_000b:  ret
}

但是对于上面的泛型代码,更有效的 IL 表示应该是:

  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  ret

从约束中可知,该值被装箱到引用类型中。 Unbox.any 操作码是完全多余的,因为在 box 操作码之后,IL 堆栈中的值将已经是对 !!U 的有效引用,这可以无需拆箱即可使用。

为什么 C# 3.0 编译器不使用约束元数据来生成更高效的通用代码? Unbox.any 的开销很小(仅慢 4-5 倍),但为什么不在这种情况下发出更好的代码呢?

Let's examine the MSIL code generated for the following generic method:

public static U BoxValue<T, U>(T value)
  where T : struct, U
  where U : class
{
  return value;
}

Look:

.method public hidebysig static !!U  BoxValue<valuetype .ctor
 ([mscorlib]System.ValueType, !!U) T,class U>(!!T 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  unbox.any  !!U
  IL_000b:  ret
}

But for generic code above, the more efficient IL representation should be:

  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  ret

It is known from the constraints that the value is boxed into reference type. Unbox.any opcode is completely redundant because after box opcode the value at IL stack will already be a valid reference to !!U, that can be used without any unboxing.

Why does C# 3.0 compiler doesn't use constraints metadata to emit more efficient generic code? Unbox.any gives a small overhead (just 4x-5x slower), but why not emit better code in this scenario?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

兮子 2024-08-16 21:27:15

看起来编译器这样做是因为验证器存在一些问题。

您希望编译器生成的 IL 是不可验证的,因此 C# 编译器无法生成它(“不安全”上下文之外的所有 C# 代码都应该是可验证的)。

“验证类型兼容性”的规则在 Ecma 规范第 III 部分第 1.8.1.2.3 节中给出。

他们说类型“S”与类型“T”或 (S := T) 验证兼容,使用以下规则:

  1. [:= 是自反的] 对于所有验证类型 S,S := S
  2. [:= 是传递的] 对于所有验证类型 S、T 和 U,如果 S := T 且 T := U,则 S := U。
  3. S := T 如果 S 是 T 的基类或由 T 实现的接口,而 T 不是一个值类型。
  4. object := T 如果 T 是接口类型。
  5. S := T 如果 S 和 T 都是接口并且 T 的实现需要实现
    S 的
  6. S := null 如果 S 是对象类型或接口
  7. S[] := T[] 如果 S := T 并且数组要么都是向量(从零开始,秩为 1),要么都不是
    是一个向量并且两者具有相同的秩。 (此规则处理数组协方差。)
  8. 如果 S 和 T 是方法指针,则 S := T 如果签名(返回类型、参数类型和
    调用约定)是相同的。

在这些规则中,唯一可能适用于本例的规则是#3。

但是,#3 不适用于您的代码,因为“U”不是“T”的基类,也不是“T”的基接口,因此“or”检查返回 false。

这意味着需要执行某些指令才能将装箱的 T 转换为 U 以通过验证程序。

我同意你的观点,应该更改验证规则,以便生成你想要的代码实际上是可验证的。

然而从技术上讲,编译器正在根据 ECMA 规范做“正确”的事情。

您应该向 Microsoft 的某人提交错误。

It looks like the compiler does this because of some issues with the verifier.

The IL that you would like the compiler to generate is not verifiable, and so the C# compiler can't generate it (all C# code outside of "unsafe" contexts should be verifiable).

The rules for "verification type compatibility" are given in Section 1.8.1.2.3, Partion III of the Ecma spec.

They say that a type 'S' is verification compatible with a type 'T' or (S := T) using the following rules:

  1. [:= is reflexive] For all verification types S, S := S
  2. [:= is transitive] For all verification types S, T, and U if S := T and T := U, then S := U.
  3. S := T if S is the base class of T or an interface implemented by T and T is not a value type.
  4. object := T if T is an interface type.
  5. S := T if S and T are both interfaces and the implementation of T requires the implementation
    of S
  6. S := null if S is an object type or an interface
  7. S[] := T[] if S := T and the arrays are either both vectors (zero-based, rank one) or neither
    is a vector and both have the same rank. (This rule deals with array covariance.)
  8. If S and T are method pointers, then S := T if the signatures (return types, parameter types and
    calling convention) are the same.

Of these rules, the only one that might be applicable in this case is #3.

However, #3 does not apply to your code, because 'U' is not a base class of 'T', and it is not a base interface of 'T', so the 'or' check returns false.

This means that SOME instruction needs to be executed in order to convert a boxed T into a U in a way that will pass the verifier.

I would agree with you that the verification rules should be changed, so that generating the code you want is actually verifiable.

Technically, however, the compiler is doing the "correct" thing based on the ECMA spec.

You should file a bug with somebody at Microsoft.

风启觞 2024-08-16 21:27:15

这些约束看起来很奇怪:

where T : struct, U
where U : class

T 是值类型,但同时必须继承自引用类型 U。我想知道什么类型可以满足上述约束并允许我们调用这个方法。

These constraints look strange:

where T : struct, U
where U : class

T is a value type but in the same time must inherit from U which is a reference type. I wonder what types could satisfy the above constraints and allow us to call this method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文