禁止发出 localsinit 标志。Suppress emitting of localsinit flag.

  • [x] 建议[x] Proposed
  • [] 原型:未启动[ ] Prototype: Not Started
  • [] 实现:未启动[ ] Implementation: Not Started
  • [] 规范:未启动[ ] Specification: Not Started

总结Summary

允许禁止 localsinit 通过属性发出标志 SkipLocalsInitAttributeAllow suppressing emit of localsinit flag via SkipLocalsInitAttribute attribute.

动机Motivation

背景Background

每个不包含引用的 CLR 规范局部变量不会通过 VM/JIT 初始化为特定值。Per CLR spec local variables that do not contain references are not initialized to a particular value by the VM/JIT. 在不初始化的情况下读取此类变量是类型安全的,但此行为是未定义的,并且是特定于实现的。Reading from such variables without initialization is type-safe, but otherwise the behavior is undefined and implementation specific. 通常未初始化的局部变量包含保留在堆栈帧现在占用的内存中的任何值。Typically uninitialized locals contain whatever values were left in the memory that is now occupied by the stack frame. 这可能导致不确定的行为,并且难以重现 bug。That could lead to nondeterministic behavior and hard to reproduce bugs.

可以通过两种方法来 "分配" 局部变量:There are two ways to "assign" a local variable:

  • 通过存储值或by storing a value or
  • 通过指定 localsinit 标志,可将分配给本地内存池的所有内容转换为零初始化说明:这包括本地变量和 stackalloc 数据。by specifying localsinit flag which forces everything that is allocated form the local memory pool to be zero-initialized NOTE: this includes both local variables and stackalloc data.

不建议使用未初始化的数据,并且在可验证代码中不允许使用。Use of uninitialized data is discouraged and is not allowed in verifiable code. 尽管可以通过流分析的方法来证明这一点,但允许验证算法非常保守,只需要 localsinit 设置。While it might be possible to prove that by the means of flow analysis, it is permitted for the verification algorithm to be conservative and simply require that localsinit is set.

在过去的 c # 编译器 localsinit 中,对声明局部变量的所有方法发出标志。Historically C# compiler emits localsinit flag on all methods that declare locals.

尽管 c # 采用了明确的赋值分析,这比 CLR 规范所需的方法更严格 (c # 还需要考虑对局部变量) 的作用域,但并不是严格保证生成的代码是可以正式验证的:While C# employs definite-assignment analysis which is more strict than what CLR spec would require (C# also needs to consider scoping of locals), it is not strictly guaranteed that the resulting code would be formally verifiable:

  • CLR 和 c # 规则可能不同意传递 local as 参数是否为 out useCLR and C# rules may not agree on whether passing a local as out argument is a use.
  • 已知条件时,CLR 和 c # 规则可能不同意条件分支的处理 (常量传播) 。CLR and C# rules may not agree on treatment of conditional branches when conditions are known (constant propagation).
  • CLR 可能也只需要 localinits ,因为这是允许的。CLR could as well simply require localinits, since that is permitted.

问题Problem

在高性能的应用程序中,强制的零初始化的成本可能比较明显。In high-performance application the cost of forced zero-initialization could be noticeable. 使用时,此方法尤其明显 stackallocIt is particularly noticeable when stackalloc is used.

在某些情况下,在后续赋值时,JIT 可以 elide 单个局部变量的初始零初始化。In some cases JIT can elide initial zero-initialization of individual locals when such initialization is "killed" by subsequent assignments. 并非所有 Jit 都这样做,因此这种优化有限制。Not all JITs do this and such optimization has limits. 它不有助于 stackallocIt does not help with stackalloc.

为了说明问题是实际问题,有一个已知 bug,其中不包含任何局部变量的方法 IL 不会有 localsinit 标志。To illustrate that the problem is real - there is a known bug where a method not containing any IL locals would not have localsinit flag. 用户通过 stackalloc 将此类方法加入到此类方法中来利用 bug,以避免初始化成本。The bug is already being exploited by users by putting stackalloc into such methods - intentionally to avoid initialization costs. 这是因为,缺少 IL 局部变量是不稳定的指标,可能因 codegen 策略中的更改而异。That is despite the fact that absence of IL locals is an unstable metric and may vary depending on changes in codegen strategy. Bug 应该是固定的,用户应获得更多记录并可靠的方式来禁止标志。The bug should be fixed and users should get a more documented and reliable way of suppressing the flag.

详细设计Detailed design

允许将指定 System.Runtime.CompilerServices.SkipLocalsInitAttribute 为指示编译器不发出标志的方式 localsinitAllow specifying System.Runtime.CompilerServices.SkipLocalsInitAttribute as a way to tell the compiler to not emit localsinit flag.

这种情况的最终结果将是 JIT 不能初始化局部变量,这在大多数情况下都是在 c # 中 unobservable。The end result of this will be that the locals may not be zero-initialized by the JIT, which is in most cases unobservable in C#.
除了该数据以外,还 stackalloc 不会初始化为零。In addition to that stackalloc data will not be zero-initialized. 这无疑是显而易见的,但也是最具打动的方案。That is definitely observable, but also is the most motivating scenario.

允许和识别的属性目标为: MethodPropertyModule 、、、 Class Struct InterfaceConstructorPermitted and recognized attribute targets are: Method, Property, Module, Class, Struct, Interface, Constructor. 但是,编译器不需要为列出的目标定义该属性,也不会小心定义该属性的程序集。However compiler will not require that attribute is defined with the listed targets nor it will care in which assembly the attribute is defined.

如果在容器 (上指定了 attribute,并 class module 为嵌套方法 ) 包含方法,则该标志会影响容器中包含的所有方法。When attribute is specified on a container (class, module, containing method for a nested method, ...), the flag affects all methods contained within the container.

合成方法从逻辑容器/所有者继承标志。Synthesized methods "inherit" the flag from the logical container/owner.

标志仅影响实际方法体的 codegen 策略。The flag affects only codegen strategy for actual method bodies. 亦.I.E. 标志对抽象方法不起作用,并且不会传播到重写/实现方法。the flag has no effect on abstract methods and is not propagated to overriding/implementing methods.

这是显式的 编译器功能 ,而 不是语言功能This is explicitly a compiler feature and not a language feature.
类似于编译器命令行开关,该功能控制特定 codegen 策略的实现细节,而无需 c # 规范。Similarly to compiler command line switches the feature controls implementation details of a particular codegen strategy and does not need to be required by the C# spec.

缺点Drawbacks

  • 旧的/其他编译器可能不接受属性。Old/other compilers may not honor the attribute. 忽略属性的行为是兼容的。Ignoring the attribute is compatible behavior. 只有可能会导致轻微的性能下降。Only may result in a slight perf hit.

  • 不带标志的代码 localinits 可能会触发验证失败。The code without localinits flag may trigger verification failures. 要求提供此功能的用户通常不关心具有可验证性。Users that ask for this feature are generally unconcerned with verifiability.

  • 在更高级别上应用属性,而不是单个方法具有非本地效果,使用时可观察到此效果 stackallocApplying the attribute at higher levels than an individual method has nonlocal effect, which is observable when stackalloc is used. 但这是最常请求的方案。Yet, this is the most requested scenario.

备选方法Alternatives

  • localinits在上下文中声明方法时省略标志 unsafeomit localinits flag when method is declared in unsafe context. 这可能会导致在的情况下,从确定性到不确定性的无提示和危险行为更改 stackallocThat could cause silent and dangerous behavior change from deterministic to nondeterministic in a case of stackalloc.

  • 忽略 localinits 标志 always。omit localinits flag always. 甚至更糟。Even worse than above.

  • 省略 localinits 标志 stackalloc ,除非在方法体中使用。omit localinits flag unless stackalloc is used in the method body. 不处理请求最多的方案,可能会打开无法验证的代码,而无需恢复返回的选项。Does not address the most requested scenario and may turn code unverifiable with no option to revert that back.

未解决的问题Unresolved questions

  • 该属性是否应实际发出到元数据?Should the attribute be actually emitted to metadata?

设计会议Design meetings

尚无。None yet.