为什么编译器发出盒指令来比较引用类型的实例? [英] Why the compiler emits box instructions to compare instances of a reference type?

查看:142
本文介绍了为什么编译器发出盒指令来比较引用类型的实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里是一个简单的通用类型,具有约束为引用类型的唯一通用参数:

Here is a simple generic type with a unique generic parameter constrained to reference types:

class A<T> where T : class
{
    public bool F(T r1, T r2)
    {
        return r1 == r2;
    }
}

生成的 IL csc.exe是:

The generated IL by csc.exe is :

ldarg.1
box        !T
ldarg.2
box        !T
ceq

因此,每个参数

So each parameter is boxed before proceeding with the comparison.

但是如果约束表明T永远不是值类型,为什么编译器试图将 r1 r2

But if the constraint indicates that "T" should never be a value type, why is the compiler trying to box r1 and r2 ?

推荐答案

它需要满足生成的IL的可验证性约束。请注意,无法验证并不一定意味着不正确。只要它的安全上下文允许运行不可验证的代码,它就工作正常没有指令。验证是保守的,并且基于固定的规则集(如可达性)。

It's required to satisfy the verifiability constraints for the generated IL. Note that unverifiable doesn't necessarily mean incorrect. It works just fine without the box instruction as long as its security context allows running unverifiable code. Verification is conservative and is based on a fixed rule set (like reachability). To simplify things, they chose not to care about presence of generic type constraints in the verification algorithm.


通用语言基础结构规范(ECMA-335)



< h3>第9.11节:通用参数的约束

...
通用参数的约束只限制类属参数
可能是实例化。
验证(参见分区三)要求
a通用参数通过满足约束提供的字段,属性或方法,不能
通过通用参数直接访问/调用,除非它是第一个盒子

(参见分区III)或 callvirt 前缀为 constrained 前缀指令。 ...

Common Language Infrastructure Specification (ECMA-335)

Section 9.11: Constraints on generic parameters

... Constraints on a generic parameter only restrict the types that the generic parameter may be instantiated with. Verification (see Partition III) requires that a field, property or method that a generic parameter is known to provide through meeting a constraint, cannot be directly accessed/called via the generic parameter unless it is first boxed (see Partition III) or the callvirt instruction is prefixed with the constrained prefix instruction. ...

删除说明将导致无法验证的代码: / p>

Removing the box instructions will result in unverifiable code:

.method public hidebysig instance bool 
       F(!T r1,
         !T r2) cil managed
{
   ldarg.1
   ldarg.2
   ceq
   ret
}


c:\Users\Mehrdad\Scratch>peverify sc.dll

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [c:\Users\Mehrdad\Scratch\sc.dll : A`1[T]::F][offset 0x00000002][fo
und (unboxed) 'T'] Non-compatible types on the stack.
1 Error(s) Verifying sc.dll






UPDATE(对注释的回答)如上所述,可验证性不等同于正确性(这里我从类型安全的角度谈论正确性)。 可验证的程序是正确程序的严格子集(即所有可验证程序都是正确的,但有正确的程序不可验证)。因此,可验证性是比正确性更强的属性。由于C#是图灵完备的语言,因此 Rice的定理说明,证明程序是正确的在一般情况下是不可判定的。


UPDATE (Answer to the comment): As I mentioned above, verifiability is not equivalent to correctness (here I'm talking about "correctness" from a type-safety point of view). Verifiable programs are a strict subset of correct programs (i.e. all verifiable programs are demonstrably correct, but there are correct programs that are not verifiable). Thus, verifiability is a stronger property than correctness. Since C# is a Turing-complete language, Rice's theorem states that proving that programs are correct is undecidable in general case.

让我们回到我的可达性比喻,因为它更容易解释。假设你正在设计C#。有一件事已经考虑的是什么时候发出关于无法访问的代码的警告,并在优化程序中删除该段代码,但如何将检测所有不可达的代码?再次,莱斯定理说,你不能为所有的节目。例如:

Let's get back to my reachability analogy as it's easier to explain. Assume you were designing C#. One thing have thought about is when to issue a warning about unreachable code, and to remove that piece of code altogether in the optimizer, but how you are going to detect all unreachable code? Again, Rice's theorem says you can't do that for all programs. For instance:

void Method() {
    while (true) {
    }
    DoSomething();  // unreachable code
}

这是C#编译器实际警告的东西。但它不会警告:

This is something that C# compiler actually warns about. But it doesn't warn about:

bool Condition() {
   return true;
}

void Method() {
   while (Condition()) {
   }
   DoSomething();  // no longer considered unreachable by the C# compiler
}

在后一种情况下流不会到达那条线。可以认为编译器可以静态地证明 DoSomething 在这种情况下是不可达的,但它不是。为什么?关键是你不能对所有程序都这样做,所以你应该在某一点绘制线条。在此阶段,您必须定义可判定属性,并将其称为可达性。例如,对于可达性,C#坚持常量表达式,不会看到函数的内容。简单的分析和设计一致性是决定绘制线条的重要目标。

A human can prove that control flow never reaches that line in the latter case. One could argue that the compiler could statically prove DoSomething is unreachable in this case too, but it doesn't. Why? The point is you can't do that for all programs, so you should draw the line at some point. At this stage, you have to define a decidable property and call it "reachability". For instance, for reachability, C# sticks to constant expressions and won't look at the contents of the functions at all. Simplicity of analysis and design consistency are important goals in deciding where to draw the line.

回到我们的可验证性概念,这是一个类似的问题。可验证性与正确性不同,是可判定的属性。作为运行时设计器,您必须决定如何定义可验证性,基于性能注意事项,易于实现,易于规范,一致性,使编译器能够轻松生成可验证代码。像大多数设计决策一样,它涉及很多权衡。最终,CLI设计人员决定,当他们检查可验证性时,他们不喜欢看通用约束。

Going back to our verifiability concept, it's a similar problem. Verifiability, unlike correctness, is a decidable property. As the runtime designer, you have to decide how to define verifiability, based on performance considerations, easy of implementation, ease of specification, consistency, making it easy for the compiler to confidently generate verifiable code. Like most design decisions, it involves a lot of trade-offs. Ultimately, the CLI designers have decided that they prefer not too look at generic constraints at all when they are checking for verifiability.

这篇关于为什么编译器发出盒指令来比较引用类型的实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆