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-carry
instruçã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)
Mas o MIPS não salva nada aqui. Eles acabaram de mudar o problema para outro lugar. No MIPS, há uma branch-on-equal
instrução. Isso significa que a instrução de ramificação deve realmente ter um estágio ALU (incluindo algo como uma xor
operação bit a bit seguida de a nor
para 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-equal
instruçã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.)
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.
fonte
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,r2
isso 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.fonte
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.
fonte