Suprimir a emissão do sinalizador localsinit
.
Observação
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Language Design Meeting (LDM).
Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .
Resumo
Permitir suprimir a emissão do indicador localsinit
através do atributo SkipLocalsInitAttribute
.
Motivação
Contexto geral
Por especificação CLR, as variáveis locais que não contêm referências não são inicializadas com um valor específico pela VM/JIT. A leitura de tais variáveis sem inicialização é segura para o tipo, mas, caso contrário, o comportamento é indefinido e específico da implementação. Normalmente, as variáveis locais não inicializadas contêm valores residuais na memória que agora está ocupada pela estrutura da pilha. Isso pode levar a um comportamento não determinista e a bugs difíceis de reproduzir.
Há duas maneiras de "atribuir" uma variável local:
- armazenando um valor ou
- especificando o sinalizador
localsinit
, que força tudo o que é alocado do pool de memória local, a ser inicializados a zero. Nota: isso inclui tanto variáveis locais quanto dadosstackalloc
.
O uso de dados não inicializados é desencorajado e não é permitido em código verificável. Embora possa ser possível provar isso por meio da análise de fluxo, é permitido que o algoritmo de verificação seja conservador e simplesmente exija que localsinit
seja definido.
Historicamente, o compilador C# emite o sinalizador localsinit
em todos os métodos que declaram locais.
Embora o C# empregue a análise de atribuição definitiva que é mais rigorosa do que a especificação CLR exigiria (o C# também precisa considerar o escopo dos locais), não é estritamente garantido que o código resultante seria formalmente verificável:
- As regras CLR e C# podem não concordar se passar um argumento local como
out
é umause
. - As regras CLR e C# podem não concordar com o tratamento de ramificações condicionais quando as condições são conhecidas (propagação constante).
- O CLR poderia muito bem simplesmente exigir
localinits
, uma vez que isso é permitido.
Problema
Em aplicativos de alto desempenho, o custo da inicialização zero forçada pode ser percetível. É particularmente percetível quando stackalloc
é usado.
Em alguns casos, o JIT pode evitar a inicialização a zero de locais individuais quando essa inicialização é eliminada por atribuições subsequentes. Nem todas as JITs fazem isso e essa otimização tem limites. Não ajuda com stackalloc
.
Para ilustrar que o problema é real - existe um bug conhecido onde um método que não contém nenhuma variável local IL
não teria o indicador localsinit
. O bug já está sendo explorado pelos usuários, colocando stackalloc
em tais métodos - intencionalmente para evitar custos de inicialização. Apesar de a ausência de variáveis locais IL
ser uma métrica instável e poder variar dependendo das mudanças na estratégia de geração de código.
O bug deve ser corrigido e os usuários devem obter uma maneira mais documentada e confiável de suprimir o sinalizador.
Projeto detalhado
Permita especificar System.Runtime.CompilerServices.SkipLocalsInitAttribute
para indicar ao compilador que não emita o sinalizador localsinit
.
O resultado final disso será que as variáveis locais podem não ser inicializadas a zero pelo JIT, o que na maioria dos casos não é observável em C#.
Além disso, os dados stackalloc
não serão inicializados com zero. Isso é definitivamente observável, mas também é o cenário mais motivador.
Os alvos de atributos permitidos e reconhecidos são: Method
, Property
, Module
, Class
, Struct
, Interface
, Constructor
. No entanto, o compilador não exigirá que o atributo seja definido com os destinos listados nem se importará com qual assembly o atributo está definido.
Quando o atributo é especificado em um contêiner (class
, module
, contendo método para um método aninhado, ...), o sinalizador afeta todos os métodos contidos no contêiner.
Os métodos sintetizados "herdam" o sinalizador do contêiner/proprietário lógico.
O indicador afeta apenas a estratégia de geração de código para corpos de método reais. Ou seja, a bandeira não tem efeito sobre métodos abstratos e não é propagada para métodos de substituição/implementação.
Este é explicitamente um recurso de compilador e não um recurso de linguagem.
Da mesma forma que as opções de linha de comando do compilador, o recurso controla os detalhes de implementação de uma estratégia específica de codegen e não precisa ser exigido pela especificação C#.
Desvantagens
Compiladores antigos/outros podem não honrar o atributo. Ignorar o atributo é um comportamento compatível. Pode apenas resultar em um ligeiro impacto de desempenho.
O código sem o sinalizador
localinits
pode desencadear falhas de verificação. Os usuários que pedem esse recurso geralmente não estão preocupados com a verificabilidade.A aplicação do atributo em níveis mais altos do que um método individual tem efeito não local, que é observável quando
stackalloc
é usado. No entanto, este é o cenário mais solicitado.
Alternativas
omitir o sinalizador
localinits
quando o método é declarado no contextounsafe
. Isso poderia causar uma mudança de comportamento silenciosa e perigosa de determinista para não determinista em um caso destackalloc
.omitir sempre o indicador
localinits
. Ainda pior do que acima.Omitir o sinalizador
localinits
, a menos que se usestackalloc
no corpo do método. Não aborda o cenário mais solicitado e pode tornar o código não verificável sem opção para reverter isso.
Questões por resolver
- O atributo deve ser realmente emitido nos metadados?
Reuniões de design
Nenhum ainda.
C# feature specifications