Eu acho que estou procurando uma resposta para uma pergunta trivial. Estou tentando entender por que a arquitetura MIPS usa um valor "zero" explícito em um registro, quando você pode conseguir a mesma coisa apenas usando XOR em qualquer registro. Pode-se dizer que a operação já foi feita para você; no entanto, não consigo imaginar uma situação em que você usaria muitos valores "zero". Li os artigos originais de Hennessey e, na verdade, atribui um zero sem nenhuma justificativa real.
Existe uma razão lógica para ter uma atribuição binária codificada de zero?
atualização: em 8k de um executável do xc32-gcc para o núcleo MIPS no PIC32MZ, tenho uma única instância de "zero".
add t3,t1,zero
a resposta real: concedeu a recompensa à pessoa que tinha as informações sobre MIPS e códigos de condição. A resposta realmente está na arquitetura MIPS para condições. Embora eu inicialmente não quisesse atribuir tempo para isso, revisei a arquitetura do opensparc , MIPS-V e OpenPOWER (este documento era interno) e aqui estão os resultados resumidos. O registro R0 é necessário para comparação nas filiais devido à arquitetura do pipeline.
- número inteiro comparado com zero e ramo (bgez, bgtz, blez, bltz)
- número inteiro compara dois registros e ramificações (beq, bne)
- número inteiro compara dois registros e armadilha (teq, tge, tlt, tne)
- integer compare register e imediato e trap (teqi, tgei, tlti, tnei)
Simplesmente se resume à aparência do hardware na implementação. No manual do MIPS-V, há uma cotação não referenciada na página 68:
As ramificações condicionais foram projetadas para incluir operações de comparação aritmética entre dois registros (como também são feitas no PA-RISC e no Xtensa ISA), em vez de usar códigos de condição (x86, ARM, SPARC, PowerPC) ou para comparar apenas um registro contra zero ( Alpha, MIPS) ou dois registros apenas para igualdade (MIPS). Esse projeto foi motivado pela observação de que uma instrução combinada de comparação e ramificação se transforma em um pipeline regular, evita o estado adicional do código de condição ou o uso de um registro temporário e reduz o tamanho do código estático e o rastreamento dinâmico de busca de instruções. Outro ponto é que as comparações com zero requerem atraso não trivial do circuito (especialmente após a mudança para a lógica estática em processos avançados) e, portanto, são quase tão caras quanto a magnitude aritmética compara. Outra vantagem de uma instrução de comparação e ramificação fundida é que as ramificações são observadas mais cedo no fluxo de instruções front-end e, portanto, podem ser previstas mais cedo. Talvez haja uma vantagem em um projeto com códigos de condição no caso em que várias ramificações podem ser obtidas com base nos mesmos códigos de condição, mas acreditamos que esse caso seja relativamente raro.
O documento MIPS-V não é encontrado no autor da seção citada. Agradeço a todos pelo tempo e consideração.
fonte
Respostas:
O registro zero nas CPUs RISC é útil por dois motivos:
É uma constante útil
Dependendo das restrições do ISA, você não pode usar um literal em algumas instruções de codificação, mas pode ter certeza de que pode usá-lo
r0
para obter 0.Pode ser usado para sintetizar outras instruções
Este é talvez o ponto mais importante. Como designer de ISA, você pode trocar um registro de uso geral por um registro zero para poder sintetizar outras instruções úteis. A sintetização das instruções é boa porque, com menos instruções reais, você precisa de menos bits para codificar uma operação em um código de operação, o que libera espaço no espaço de codificação da instrução. Você pode usar esse espaço para, por exemplo, maiores desvios de endereço e / ou literais.
A semântica do registro zero é como
/dev/zero
nos sistemas * nix: tudo o que é escrito nele é descartado e você sempre lê 0.Vamos ver alguns exemplos de como podemos fazer pseudo-instruções com a ajuda do
r0
registro zero:O caso do MIPS
Eu olhei mais de perto o conjunto de instruções do MIPS. Existem algumas pseudo-instruções que são usadas
$zero
; eles são usados principalmente para galhos. Aqui estão alguns exemplos do que eu encontrei:Quanto ao motivo pelo qual você encontrou apenas uma instância do
$zero
registro na sua desmontagem, talvez seja o seu desmontador que seja inteligente o suficiente para transformar seqüências conhecidas de instruções em suas pseudo-instruções equivalentes.O registro zero é realmente útil?
Bem, aparentemente, o ARM considera que ter um registro zero é útil o suficiente para que, em seu (um pouco) novo núcleo ARMv8-A, que implementa o AArch64, agora haja um registro zero no modo de 64 bits; não havia um registro zero antes. (O registro é um pouco especial, porém, em alguns contextos de codificação, é um registro zero; em outros, ele designa o ponteiro da pilha )
fonte
slt
,slti
,sltu
).A maioria das implementações ARM / POWER / SPARC possui um registro RAZ oculto
Você pode pensar que o ARM32, SPARC etc não tem um registro 0, mas na verdade eles têm! No nível da microarquitetura, a maioria dos engenheiros de design da CPU adiciona um registro 0 que pode ser invisível ao software (o registro zero do ARM é invisível) e usa esse registro zero para otimizar a decodificação de instruções.
Considere um projeto típico típico do ARM32 que possui um registro invisível do software, digamos R16 conectado a 0. Considere a carga do ARM32, muitos casos de instrução de carregamento do ARM32 se enquadram em uma dessas formas (Ignore a indexação pré-pós por um tempo para manter a discussão simples) ) ...
Dentro do processador, isso decodifica em geral
antes de entrar no estágio de emissão em que os registros são lidos. Observe que rx representa o registro para gravar novamente o endereço atualizado. Aqui estão alguns exemplos de decodificação:
No nível do circuito, as três cargas são na verdade a mesma instrução interna e uma maneira fácil de obter esse tipo de ortogonalidade é criar um registrador de solo R16. Como o R16 está sempre aterrado, essas instruções naturalmente decodificam corretamente sem nenhuma lógica extra. O mapeamento de uma classe de instruções para um único formato interno ajuda muito nas implementações superescalares, pois reduz a complexidade lógica.
Outro motivo é uma maneira simplificada de jogar fora as gravações. As instruções podem ser desabilitadas simplesmente configurando o registro de destino e os sinalizadores para R16. Não há necessidade de criar nenhum outro sinal de controle para desativar a gravação, etc.
A maioria das implementações de processador, independentemente da arquitetura, acaba com um modelo de registro RAZ no início do pipeline. O pipeline do MIPS começa essencialmente em um ponto que, em outras arquiteturas, teria alguns estágios.
O MIPS fez a escolha certa
Assim, um registro de leitura como zero é quase obrigatório em qualquer implementação de processador moderna e o MIPS tornando-o visível para o software é definitivamente um ponto positivo, dado que ele simplifica a lógica de decodificação interna. Os projetistas de processadores MIPS não precisam adicionar um registro RAZ extra, já que $ 0 já está no chão. Como o RAZ está disponível para o montador, muitas instruções psuedo estão disponíveis para o MIPS e pode-se pensar nisso como empurrar parte da lógica de decodificação para o próprio montador em vez de criar formatos dedicados para cada tipo de instrução para ocultar o registro RAZ do software como em outras arquiteturas. O registro do RAZ é uma boa ideia e é por isso que o ARMv8 o copiou.
Se o ARM32 tivesse um registro de US $ 0, a lógica de decodificação se tornaria mais simples e a arquitetura teria sido muito melhor em termos de velocidade, área e potência. Por exemplo, das três versões do LDR apresentadas acima, apenas 2 formatos seriam necessários. Da mesma forma, não há necessidade de reservar a lógica de decodificação para as instruções MOV e MVN. Além disso, o CMP / CMN / TST / TEQ se tornaria redundante. Também não seria necessário diferenciar entre multiplicação curta (MUL) e longa (UMULL / SMULL), pois a multiplicação curta poderia ser considerada como multiplicação longa com o registro alto definido como $ 0 etc.
Como o MIPS foi inicialmente projetado por uma equipe pequena, a simplicidade do design foi importante e, portanto, US $ 0 foram escolhidos explicitamente no espírito do RISC. O ARM32 mantém muitos recursos tradicionais do CISC no nível arquitetural.
fonte
Disclamer: Eu realmente não conheço o MIPS assembler, mas o registro de valor 0 não é exclusivo dessa arquitetura e acho que é usado da mesma maneira que em outras arquiteturas RISC que conheço.
XORing um registro para obter 0 custará uma instrução, enquanto usar um registro de valor 0 predefinido não.
Por exemplo, as
mov RX, RY
instruções são frequentemente implementadas comoadd RX, RY, R0
. Sem um registro de valor 0, você precisariaxor RZ, RZ
sempre que quiser usarmov
.Outro exemplo é a
cmp
instrução e suas variantes (como "comparar e pular", "comparar e mover" etc.), ondecmp RX, R0
é usado para testar números negativos.fonte
MOV Rx,Ry
comoAND Rx,Ry,Ry
?mov RX, Imm
oumov RX, mem[RY]
se o seu conjunto de instruções suportar apenas um único valor imediato e um único acesso à memória por instrução.mov
é um péssimo exemplo; você pode implementá-lo com um 0 imediato em vez de um registro zero. por exemploori dst, src, 0
. Mas sim, você precisaria de um código de operação para o mov-imediato se registrar, se não o tivesseaddiu $dst, $zero, 1234
, comolui
os 16 bits inferiores em vez dos 16 superiores. E você não poderia usarnor
ousub
criar um operando not / neg .Amarrar algumas pistas no final do seu banco de registro é barato (mais barato do que torná-lo um registro completo).
Fazer o xor real leva um pouco de energia e tempo para trocar os portões e depois armazená-lo no registro, por que pagar esse custo quando um valor 0 existente pode facilmente estar disponível.
Os cpus modernos também têm um registro de valor 0 (oculto) que podem ser usados como resultado de uma
xor eax eax
instrução através da renomeação do registro.fonte
R0
não está no aterramento de alguns fios, mas no fato de que você precisa reservar um código para ele em todas as instruções que tratam dos registros.std::memory_order_consume
) exigem que o XOR propague a dependência.lui
mas não o deslocamento da esquerda para 16. Portanto, você ainda pode colocar um número pequeno em um registro com uma instrução. Permitir apenas zero com uma falsa dependência seria insano. (O MIPS normal cria valores diferentes de zero comaddiu $dst, $zero, 1234
ouori
, portanto, seu argumento de "custo de energia" é interrompido. Se você quiser evitar o acionamento de uma ALU, inclua um código de operação para registro imediato em movimento em vez de ter o software ADD ou OR um imediato com zero.)