por que as arquiteturas de CPU usam um registro de sinalizadores (vantagens?)
Algumas CPUs possuem um registro de sinalizadores (ARM, x86, ...), outras não (MIPS, ...). Qual é a vantagem de ter uma instrução CMP para atualizar o registro de sinalizadores seguido por uma instrução de ramificação, em vez de usar um registro zero e ramificações condicionais para verificar se há sinal, estouro, etc.?
— mundo modelo
Nas microarquiteturas modernas com renomeação de registradores, o custo de implementação de flags ou não é bastante semelhante. A principal diferença que consigo pensar é que alguns sinalizadores indicam as características de um valor (o valor é negativo? O valor é zero? O valor tem paridade par ou ímpar?), Enquanto alguns representam um evento que ocorreu durante uma operação anterior (a instrução add teve uma execução ou estouro?) Isso levou a uma situação abaixo do ideal no MIPS quando você queria simular a adição de 64 bits na arquitetura de 32 bits (ou a adição de 128 bits no Arquitetura de 64 bits.) Na maioria das arquiteturas com uma bandeira de transporte, existe umadd-with-carryinstrução, que inclui o sinalizador carry da instrução add anterior. Isso torna a simulação da aritmética de precisão múltipla relativamente barata em muitas arquiteturas com registros de sinalizadores.
Por outro lado, testar um registrador de N bits para zero ou não-zero é realmente surpreendentemente caro. Para testar um registro de N bits zero, é necessário executar uma operação NOR de N bits, que requer o cálculo dos níveis lógicos . Em arquiteturas com sinalizadores, a lógica extra para o cálculo de zero / não-zero no final do estágio da ULA pode fazer com que o relógio corra mais devagar (ou forçar a ULA a ter duas operações de ciclo). Por esse motivo, acho que algumas arquiteturas, como SPARC, tinham duas versões de cada operação aritmética, uma que definia sinalizadores e outra que não.O ( logN)O(registro⁡N)
Mas o MIPS não salva nada aqui. Eles acabaram de mudar o problema para outro lugar. No MIPS, há uma branch-on-equalinstrução. Isso significa que a instrução de ramificação deve realmente ter um estágio ALU (incluindo algo como uma xoroperação bit a bit seguida de a norpara reduzir o único bit igual / não igual) antes de determinar para que lado a ramificação segue.
A arquitetura DEC Alpha tentou dividir a diferença usando um truque. O DEC Alpha não tinha registradores de sinalizadores, mas também não tinha uma branch-on-equalinstrução. Em vez disso, todas as instruções de ramificação examinam o estado de um único registro de uso geral. Há branch-on-zero, branch-on-not-zero, branch-on-less-than-zero, etc. O truque é que você pode dar a cada uso geral registar um bit 65 extra que você diz se os outros 64 bits são todos zero ou não. Isso torna mais parecido com um registro de sinalizadores: todas as instruções de ramificação olham para um único bit (que já está calculado) para tomar sua decisão, mas agora você está voltando a descobrir como calcular esse bit indicador extra de zero durante uma ALU normal ciclo. (E você ainda não pode fazer aritmética de precisão múltipla apenas observando a bandeira de transporte da operação anterior.)
As operações de configuração não-CC eram (pelo que entendi) uma otimização do compilador , permitindo que o compilador agendasse as instruções de configuração do CC mais cedo, sem que o valor fosse prejudicado por essas instruções. O PowerPC750 colocou os registradores de condição (8 registradores de 4 bits) mais próximos do front end, de modo que uma ramificação tomada atingindo o cache de instruções de destino da ramificação e tendo a condição disponível com antecedência suficiente pudesse resolver uma ramificação tomada sem penalidade. (O CRISP da AT&T também explorou a resolução inicial das agências.) A pequena quantidade e especialização dos CCs tornam isso mais prático.
Um detalhe: todos os cálculos de sinalizadores não são iguais. Imagine que sua CPU possui os sinalizadores tradicionais do NZVC. Se todas as instruções da ALU tiverem permissão para atualizar os sinalizadores, você deverá colocar a geração do sinalizador após o somador / subtrator e alguns muxes. O sinalizador Negativo é fácil, é apenas o MSB, enquanto o sinalizador Zero é caro e depende de todos os bits. Agora, se você restringir os sinalizadores às instruções Compare (e teste de bits), os sinalizadores Zero poderão ser calculados com XORs paralelos nos operandos de origem, sem aguardar o resultado da subtração. O cálculo da bandeira Z após uma adição é quase inútil.
1 Do ponto de vista do ISA
Ter instruções de teste que definem apenas os sinalizadores é apenas uma maneira de reduzir a pressão do registro em arquiteturas sem registro. Se você tiver registros suficientes, apenas modifique um deles e ignore o resultado. O truque de ter um registro 0 com o valor de entrada 0 é apenas um truque de codificação conveniente quando você possui registros suficientes para que fixar um deles em 0 seja melhor do que aumentar o número de instruções. É conveniente usá-lo também como destino (reduz o número de dependências falsas).
Codificação novamente. Se você codificar a condição em saltos, terá saltos com 3 operandos (os dois a serem comparados e o destino do salto), dois dos quais você gostaria que fossem valores imediatos, um que você gostaria que fosse tão grande quanto possível (os saltos geralmente têm seu próprio formato de codificação para que o destino possa usar o máximo de bits possível). Ou você deixa cair as possibilidades.
O uso de sinalizadores oferece mais oportunidades para defini-los. Não são apenas as operações de comparação que podem definir os sinalizadores, mas o que você quiser. (Com a ressalva de que quanto mais operações você tiver que definir sinalizadores, mais cuidado será necessário para garantir que a última operação que define os sinalizadores seja a desejada). Se você tiver sinalizadores, poderá testar o número de condições (geralmente 16) multiplicado pelo número de instruções que são capazes de configurá-los (se você não estiver usando sinalizadores, você terá o máximo de saltos condicionais que desejar. tem coisas para testar ou há coisas que você não permite testar tão facilmente (transporte ou transbordamento, por exemplo).
2 Do ponto de vista do implementador
Testar sinalizadores é fácil e pode ser feito rapidamente. Quanto mais complexo for seu teste, mais efeito ele terá no tempo de ciclo (ou na estrutura do pipeline, se você estiver em pipeline). Isso é especialmente verdadeiro para implementações mais simples: quando você chega a um processador de ponta usando todos os truques do livro, o efeito é bem mínimo.
Ter sinalizadores significa que muitas instruções têm vários resultados (o resultado natural e cada um dos sinalizadores modificados). E de um ponto de vista de microarquitetura, vários resultados são ruins (é necessário acompanhar a associação deles). Quando você tem apenas um conjunto de sinalizadores, que introduz dependências (desnecessário se o sinalizador não for usado), você precisa lidar com uma maneira ou outra. Novamente, isso é especialmente verdadeiro para implementações mais simples, quando você chega a um processador de ponta usando todos os truques do livro, as dificuldades adicionais são diminuídas pelo restante do processador.
Em uma máquina de 32 bits, uma instrução "adicionar com transportar" usada como parte de uma sequência de adição de precisão múltipla precisa aceitar 65 bits no valor de operandos e calcular uma soma de 33 bits. As especificações do registro de origem identificarão de onde devem vir os 64 bits do operando e a especificação do registro de destino dirá para onde devem ir os 32 bits inferiores do resultado, mas o que fazer com o operando "adicionar um extra" ou o bit superior do resultado? Ser permitido especificar como parte da instrução de onde o operando extra deve vir e para onde o bit de resultado extra deve ir seria moderadamente útil, mas geralmente não seria tão útil a ponto de justificar um campo extra no código de operação. Ter um "local" fixo para lidar com o sinalizador de transporte pode ser um pouco estranho do ponto de vista da programação de instruções, mas é '
Se alguém tentasse projetar um conjunto de instruções para permitir aritmética de precisão múltipla, mas cada instrução estivesse limitada a dois operandos de 32 bits e um operando de destino de 32 bits, seria possível implementar um "add" de 64 bits em quatro instruções: "set r5 a 1 se r0 + r2 carregaria ou zero, caso contrário; calcule r4 = r1 + r3; calcule r5 = r4 + r5; calcule r4 = r0 + r2 ", mas ir além disso exigiria três instruções para cada palavra adicional. Ter um sinalizador de transporte disponível como fonte e destino suplementar reduz o custo para uma instrução por palavra.
Observe que o fato de ter um bit de instrução controlar se a instrução atualiza o registrador de sinalizador pode facilitar a execução fora de ordem, uma vez que as instruções que usam ou modificam os bits de sinalizador devem manter sua sequência relativa entre si, mas instruções que não podem ser reorganizados livremente. Dada a sequência:
uma unidade de execução poderia facilmente reconhecer que a terceira instrução poderia ser executada sem ter que esperar a leitura dos dados [r1], mas se a segunda instrução fosse, adds r0,r0,r2isso só seria possível se a unidade de execução pudesse garantir que, no momento em que algo tentasse usar os sinalizadores, o sinalizador zero manteria o valor estabelecido na terceira instrução, mas o sinalizador de transporte manteria o valor na segunda.
"bit de instrução controla se a instrução atualiza o registro do sinalizador": Disponível, por exemplo, no PowerPC, SPARC.
O MIPS usa "r5 = r1 + r2; defina r6 se r6 for menor que r1; r7 = r3 + r4; r5 = R5 + R6;". Algumas extensões SIMD podem usar comparações que definem todos os bits para zero ou um (ou seja, zero ou -1 dois complementam o número inteiro) para encontrar o transporte e a subtração para aplicar o transporte.
@ PaulA.Clayton: Eu acho que você quis dizer "se r5 for menor que r1". Como o MIPS lidaria com matemática mais longa? Exigiria três, mais que três ou menos que três instruções por palavra?
@ Supercat Sim, isso deveria ter sido "set r6 se r5 for menor que r1"!
@ PaulA.Clayton: Como alguém adicionaria, por exemplo, dois números de 64 palavras (2048 bits) em um MIPS de 32 bits? Existe alguma maneira eficiente de lidar com os carregamentos dentro e fora dos estágios intermediários?
Resposta simples ... operação rápida e barata de memória que não requer absolutamente nenhum uso interno de ônibus, exceto a própria instrução. Pode ser usado como um bool de pilha sem uma pilha ou um bit de processo, sem memória.
Esta resposta é bastante clara nos detalhes. Respostas longas não são necessariamente necessárias, mas algo mais detalhado seria uma melhoria distinta.
definir um sinalizador ou comparar um valor de sinalizador é uma única instrução sem nenhuma outra informação na forma de argumentos que seriam incluídos no código do assembly. Os sinalizadores também são resultado da operação ou teste do processador e podem ser usados ​​com eficiência para ramificar. eles são o bit real que é alternado ou definido quando dois valores são comparados em registradores.