Pergunta

eu estava lendo o Manual de instruções Intel e notei que há uma instrução 'NOP' que não faz nada na CPU principal e uma instrução 'FNOP' que não faz nada na FPU.Por que existem duas instruções separadas para não fazer nada?

A única coisa diferente que vi foi que eles lançavam exceções diferentes, então você pode observar uma exceção do FNOP para detectar se há uma FPU disponível.Mas não existem outros mecanismos como o CPUID para detectar isso?Que razão prática existe para ter duas instruções NOP separadas?

Foi útil?

Solução

Expandindo os comentários de Raymond Chen e Hans Passant, existem razões históricas para haver duas instruções separadas e porque elas não têm exatamente o mesmo efeito.

Nenhuma das duas instruções, NOP e FNOP, foram originalmente projetados como uma instrução explícita de não operação.O NOP instrução é na verdade apenas um apelido para a instrução XCHG AX,AX.(Ou no modo de 32 bits XCHG EAX, EAX.) Nos primeiros processadores Intel, na verdade, não fazia nada.Embora não tenha tido nenhum efeito visível externamente, internamente foi executado como um XCHG instrução, levando tantos ciclos para ser executada.O '486 foi o primeiro CPU Intel a tratá-lo especialmente, ele poderia executar um NOP em 1 ciclo, enquanto foram necessários 3 ciclos para executar qualquer outro registro a registro XCHG instrução.

Tratar XCHG AX,AX a instrução torna-se especialmente muito importante nos processadores Intel modernos.Se ele ainda estivesse realmente trocando o mesmo registro consigo mesmo, poderia introduzir travamentos no pipeline se uma instrução próxima também usasse o AX registro.Ao tratá-lo de maneira especial a CPU não acaba pensando o NOP precisa esperar por uma instrução anterior que defina AX ou que uma instrução seguinte precisa esperar pelo NOP.

Isso traz à tona o fato de que existem muitas instruções diferentes que não fazem nada, embora XCHG AX,AX é o único que é um único byte (como um caso especial do byte único de registro de troca com acumulador XCHG codificações).Freqüentemente, essas instruções são usadas como substitutos de instruções únicas para instruções consecutivas. NOP instruções, como ao alinhar o início do loop por motivos de desempenho.Por exemplo, se você quiser um NOP de 6 bytes, poderá usar LEA EAX,[EAX + 00000000].A Intel eventualmente adicionou uma instrução NOP explícita de múltiplos bytes.(Bem, não foi adicionada nem documentada oficialmente uma instrução que existia desde o Pentium Pro.) No entanto, apenas o formato de byte único é tratado de maneira especial;os NOPs de múltiplos bytes gerarão paralisações se instruções próximas usarem os mesmos registradores.

Quando a AMD adicionou suporte de 64 bits às suas CPUs, eles foram ainda mais longe. NOP não é mais o equivalente a XCHG EAX,EAX no modo de 64 bits.Um dos problemas com o conjunto de instruções da Intel é que existem muitas instruções que modificam apenas parte do registro.Por exemplo MOV BX,AX modifica apenas os 16 bits inferiores do EBX deixando os 16 bits superiores inalterados.Essas modificações parciais tornam difícil para a CPU evitar travamentos, então a AMD decide evitar isso ao usar instruções de 32 bits no modo de 64 bits.Sempre que o resultado de uma operação de 32 bits é armazenado em um registrador (64 bits), o valor é zero estendido para 64 bits para que todo o registro seja modificado.Isso significa XCHG EAX,EAX não é mais um NOP, pois limpa os 32 bits superiores do EAX (e, portanto, se você escrever explicitamente XCHG EAX,EAX, ele não pode ser montado em 0x90 e precisa usar o 87 C0 codificação).No modo de 64 bits NOP é agora um NOP explícito sem outra interpretação.


Quanto ao FNOP instrução, no 8087 original não está totalmente claro como a FPU tratou esta instrução, mas tenho certeza de que também não foi tratada como uma não operação explícita.Pelo menos um manual antigo da Intel, o Manual de referência da linguagem ASM86 documenta como fazer algo sem efeito ("armazena o topo da pilha no topo da pilha").Pela sua posição no mapa de opcode, parece que pode ser um alias para qualquer um FST ST ou FLD ST, sendo que ambos copiariam o topo da pilha para o topo da pilha.No entanto, recebeu algum tratamento especial, ele foi executado em uma média de 13 ciclos, em vez da média de 18 ou 20 ciclos para uma pilha empilhar. FST ou FLD instrução respectivamente.Se estivesse sendo tratado como uma instrução sem operação, esperaria que fosse ainda mais rápido, pois há várias instruções 8087 que podem ser executadas na metade do tempo.

Mais importante ainda FNOP instrução se comporta de maneira diferente NOP por causa de como as instruções FPU costumavam ser implementadas nos processadores Intel.A CPU em si não suportava aritmética de ponto flutuante; em vez disso, essas tarefas foram transferidas para um coprocessador de ponto flutuante opcional, originalmente o 8087.Uma das coisas boas do coprocessador é que ele executava instruções em paralelo com a CPU.No entanto, isso significa que a CPU às vezes precisa esperar que a FPU termine uma operação.A CPU espera automaticamente que termine de executar a instrução anterior antes de fornecer outra instrução, mas um programa precisaria esperar explicitamente (usando um WAIT instrução) antes que pudesse ler um resultado que o coprocessador gravou na memória.

Como o coprocessador trabalhava em paralelo, isso também significava que se uma instrução FPU gerasse uma exceção de ponto flutuante, no momento em que detectasse isso, a CPU já teria passado para executar a próxima instrução.Normalmente, quando uma instrução gera uma exceção na CPU, ela é tratada enquanto a instrução ainda está sendo executada, mas quando uma instrução FPU gera uma exceção, a CPU já concluiu a execução dessa instrução, entregando-a à FPU.Em vez de interromper a CPU e entregar a exceção de ponto flutuante de forma assíncrona, a CPU só é notificada quando espera pelo coprocessador, seja explícita ou implicitamente.

Nos processadores modernos, a FPU não é mais um coprocessador, é parte integrante da CPU.Isso significa que os programas não precisam mais esperar que a FPU grave valores na memória.No entanto, a forma como as exceções FPU são tratadas não mudou.(Acontece que entregar exceções imediatamente é difícil de implementar em CPUs modernas, então eles aproveitaram o único caso em que não era necessário.) Portanto, se uma instrução FPU anterior gerasse uma exceção de ponto flutuante não entregue, um NOP deixe a exceção não entregue, enquanto FNOP, por ser uma instrução FPU, fará uma "espera" implícita que resulta na entrega da exceção de ponto flutuante.

Este exemplo demonstra a diferença:

FLD1       ; push 1.0 onto the FPU stack
FLDZ       ; push 0.0
FDIV       ; divide 1.0 by 0.0
NOP        ; does nothing
NOP        ; does nothing
FNOP       ; signals a FP zero-divide exception and then does nothing
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top