Suprimir a emissão do sinalizador localsinit
.
Nota
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação 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 reunião de design de idioma (LDM).
Você pode saber mais sobre o processo de adoção de speclets de recursos no padrão de linguagem C# no artigo sobre as especificações de .
Resumo
Permitir a supressão da emissão do sinalizador localsinit
por meio do atributo SkipLocalsInitAttribute
.
Motivação
Contexto
Por especificação CLR, variáveis locais que não contêm referências não são inicializadas para um valor específico pela VM/JIT. A leitura dessas variáveis sem inicialização é segura por tipo, mas caso contrário, o comportamento é indefinido e específico da implementação. Normalmente, os locais não inicializados contêm os valores deixados na memória que agora está ocupada pelo quadro de pilha. Isso pode levar a um comportamento não determinístico e a erros 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 inicializado com zero. Nota: isso inclui tanto as variáveis locais quanto os dadosstackalloc
.
O uso de dados não inicializados é desencorajado e não é permitido no código verificável. Embora seja possível provar que, por meio da análise de fluxo, é permitido que o algoritmo de verificação seja conservador e simplesmente exija que localsinit
esteja definido.
Historicamente, o compilador C# emite a flag localsinit
em todos os métodos que declaram variáveis locais.
Embora o C# empregue uma análise de atribuição definida que seja mais rigorosa do que a especificação CLR exigiria (c# também precisa considerar o escopo de locais), não é estritamente garantido que o código resultante seria formalmente verificável:
- As regras CLR e C# podem não concordar se um argumento local deve ser passado pois um argumento
out
é umuse
. - 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
No aplicativo de alto desempenho, o custo da inicialização zero forçada pode ser perceptível. É particularmente perceptível quando stackalloc
é usado.
Em alguns casos, o JIT pode omitir a inicialização com zero de variáveis locais individuais quando essa inicialização é substituída por atribuições subsequentes. Nem todos os JITs fazem isso e essa otimização tem limites. Isso não ajuda com stackalloc
.
Para ilustrar que o problema é real - há um bug conhecido em que um método que não contém nenhum IL
local não teria o sinalizador localsinit
. O bug já está sendo explorado pelos usuários colocando stackalloc
nesses métodos intencionalmente para evitar custos de inicialização. Isso ocorre apesar do fato de que a ausência de elementos locais IL
é uma métrica instável e pode variar dependendo das alterações 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.
Design detalhado
Permitir especificar System.Runtime.CompilerServices.SkipLocalsInitAttribute
como uma maneira de indicar ao compilador para não emitir o flag localsinit
.
O resultado final disso será que as variáveis locais podem não ser inicializadas com zero pelo JIT, o que na maioria dos casos é imperceptível em C#.
Além disso, os dados stackalloc
não serão inicializados por zero. Isso é definitivamente observável, mas também é o cenário mais motivador.
Os destinos de atributo 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á em qual assembly o atributo está definido.
Quando o atributo é especificado em um contêiner (class
, module
, que contém o 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 sinalizador afeta apenas a estratégia de geração de código para os corpos de métodos reais. Ou seja, o sinalizador não tem efeito sobre métodos abstratos e não é propagado para substituir/implementar métodos.
Esse é explicitamente um recurso do compilador e não um recurso de linguagem .
Semelhante aos switches de linha de comando do compilador, esta funcionalidade controla os detalhes de implementação de uma estratégia específica de geração de código e não precisa ser exigida pela especificação do C#.
Inconvenientes
Compiladores antigos/outros podem não honrar o atributo. Ignorar o atributo é um comportamento compatível. Isso pode resultar apenas em um leve impacto no desempenho.
O código sem o sinalizador
localinits
pode causar falhas de verificação. Os usuários que solicitam esse recurso geralmente não estão preocupados com a verificação.Aplicar o 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
omita o sinalizador
localinits
quando o método for declarado no contextounsafe
. Isso pode causar uma alteração de comportamento silencioso e perigoso de determinístico para não determinístico em um caso destackalloc
.omita sempre o sinalizador
localinits
. Ainda pior do que acima.omita o flag
localinits
, a menos que ostackalloc
seja utilizado dentro do corpo do método. Não aborda o cenário mais solicitado e pode tornar o código inverificável sem nenhuma opção para reverter isso.
Perguntas não resolvidas
- O atributo deve ser de fato emitido para os metadados?
Reuniões de design
Nenhum ainda.
C# feature specifications