Pergunta

Alguém pode explicar estas poucas linhas de MSIL? Por que mover um valor fora da pilha de avaliação para uma variável local, apenas para movê-lo de volta imediatamente e devolvê-lo?

As seguintes cargas código MSIL um único argumento (a string), chama um método, que retorna bool e retorna esse valor bool. O que eu não entendo é por que ele chama stloc.0 para armazenar o valor de retorno do método em uma variável local, em seguida, executa uma transferência de controle incondicional explícita à linha muito próxima marcada (parece desnecessário), apenas para mover o valor de volta para a pilha de avaliação antes de devolvê-lo.

.maxstack 1
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string)
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 

Meu melhor palpite sobre por que ele faz isso é a realização de algum tipo de verificação de tipo para garantir o valor na pilha de avaliação é realmente um valor booleano antes de devolvê-lo. Mas estou à nora sobre o salto explícita à muito próxima linha; Quero dizer, não seria ir lá de qualquer maneira? O código-fonte C # para o método é apenas uma linha, que devolve o resultado do método.

Foi útil?

Solução

Se você abrir esta função no depurador, com código compilado no modo de depuração:

bool foo(string arg)
{
    return bar(arg);
}

Existem 3 pontos de quebra que você pode definir:

  1. Na cinta da função de abertura.
  2. Na linha de "retorno".
  3. Na chave de fechamento da função.

A definição de um ponto de ruptura na abertura meios de travar "pausa quando esta função get é chamado". É por isso que há uma instrução não-op no início do método. Quando o ponto de interrupção é definido na chave de abertura o depurador realmente define-lo no não-op.

Definir um ponto de quebra nos meios chave de fechamento "quebrar quando esta função saídas". Para que isso aconteça a função precisa ter uma única instrução de retorno nele do IL, onde o ponto de ruptura pode ser definido. O compilador permite que, usando uma variável temporária para armazenar o valor de retorno, e convertendo

return retVal;

para

$retTmp = retVal;
goto exit;

e injectando depois o seguinte código na parte inferior do método:

exit:
return $ret;

Além disso, quando no modo de depuração, compiladores são estúpidos sobre o código que generation.They basicamente fazer algo como:

GenerateProlog();
foreach (var statement in statements)
{
    Generate(statement);
}
GenerateEpilog();

No seu caso, você está vendo:

return foo(arg);

sendo traduzido em:

; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;

Se o compilador estava fazendo um "deslizamento otimização janela" que poderia ser capaz de olhar para esse código e perceber que havia alguma redundency.However, compiladores geralmente não fazem isso no modo de depuração. otimizações do compilador pode fazer coisas como eliminar variáveis ??e instruções de reordenamento, que torna a depuração difícil. Desde o fim de uma compilação de depuração é habilitar a depuração, não seria bom para ligar otimizações.

Em uma compilação de lançamento, o código se parece com isso. Isso porque o compilador não introduzir o código especial para permitir pontos de interrupção sobre a abertura e fechamento de chaves, que só deixa o seguinte para ser compilado:

return bar(arg);

, que acaba olhando muito simples.

Uma coisa a notar, no entanto, é que eu não acho que o compilador C # não muito deslizante otimizações janela, mesmo no varejo compilações. Isso porque a maioria dessas otimizações dependem da arquitetura do processador subjacente e assim é feito pelo compilador JIT. Fazendo as otimizações, mesmo os que são agnóstico processador, no compilador C # pode impedir a capacidade do JIT para otimizar o código (que está à procura de padrões que são gerados pela geração não otimizado código, e se vê altamente otimizado IL ele pode ficar confuso). compiladores de código de modo geral manged não fazê-las. Ele faz algumas "coisas caras" (que o JIT não quer fazer em tempo de execução), como a detecção de código morto, e análise das variáveis ??ao vivo, mas eles não abordam os problemas resolvidos por deslizamento otimização janela.

Outras dicas

Você está compilando no modo de depuração ou liberação? No modo de versão eu recebo:

.method private hidebysig static bool Test1(string arg) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string)
    L_0006: ret 
}

A ramificação que você está vendo é provavelmente para suporte depurador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top