Introdução ao nível de arquitetura de conjunto de instruções (ISA)
Introdução
Este é o primeiro de uma série de artigos em que pretendo falar sobre arquitetura de computadores para fins de aprendizado e transmissão de conhecimento. Aqui, por conveniência e gosto pessoal, apresento uma breve introdução ao nível de arquitetura de conjunto de instruções (ISA), que será aprofundado em artigos posteriores.
O que é um computador?
Antes de partirmos para o que é uma ISA, é necessário resumir, ainda que de forma bem superficial, o que é um computador.
Um computador é tão-somente uma máquina feita para resolver ou ajudar em tarefas humanas. Trata-se de uma máquina primitiva, a sua única tarefa real é realizar cálculos rapidamente, além de ser capaz de entender apenas 0s (transistor desligado) e 1s (transistor ligado).
Dado tal fato, você pode estar se perguntando: como, então, conseguimos realizar tarefas complexas, a exemplo de transmissão de vídeo e execução de jogos online? A resposta está na palavra abstração. Que é essa dita abstração? É simplesmente a organização de um computador em várias camadas, sendo cada camada superior responsável por abstrair operações mais complexas do nível abaixo e transformá-las em algo mais legível e amigável para o ser humano.
Antes de prosseguirmos, vale compreender como funciona a abstração em computadores na prática. Imagine uma escada: cada degrau representa um nível de complexidade. No topo, temos linguagens e interfaces próximas da forma como pensamos e escrevemos. Mais abaixo, instruções menos intuitivas, até chegar, por fim, aos sinais elétricos que o computador real entende – tensões de 0V ou 5V, representadas pelos valores 0 e 1, respectivamente. Observe o esquema abaixo:
if age ≥ 18 then display dialog "Adulto"
if (idade >= 18) printf("Adulto");
CMP AX, 18
JGE adulto
00111001 00010010
01110101 00000100
Um computador com múltiplos níveis funciona como diferentes máquinas virtuais, cada uma com a sua linguagem específica. Apenas a linguagem do nível mais baixo é executada diretamente pelo hardware (utilizarei esse termo para me referir aos circuitos eletrônicos de agora em diante), enquanto as demais precisam ser interpretadas ou traduzidas (compiladas) para níveis inferiores. Isso é bom para o programador que trabalha em níveis superiores, porque ele não precisa se preocupar com esses processos de tradução, já que a arquitetura do sistema garante que seus programas serão executados, independentemente de como isso acontece internamente.
Nível de arquitetura de conjunto de instruções
Agora que sabemos o que é um computador, é importante ressaltar que o esquema apresentado anteriormente trata-se apenas da ponta do iceberg em relação a onde pretendemos chegar. Computadores modernos são, na verdade, constituídos por alguns outros níveis não apresentados, como é o caso do nível de microarquitetura – que será tratado em outra ocasião – e o nível ISA (Instruction Set Architecture), do qual trataremos agora.
Criado com o intuito de padronizar a fabricação de processadores e torná-los compatíveis entre si, a ISA é uma especificação meramente abstrata que define a interface de comunicação entre hardware e software, sendo um modelo de programação oferecido pelo processador e o designador do comportamento do hardware. É a linguagem comum compreendida por compiladores e hardware. Alinhando-nos a Tanenbaum, podemos dizer que o código de ISA é o que um compilador produz.
Tendo isso em mente, é possível dizer que a ISA é responsável por definir os tipos de instruções disponíveis, os tipos de operações, modos de endereçamento, modos de operação (como usuário e kernel, que serão abordados em outros artigos), além de como o hardware se comporta do ponto de vista do programador. Alguns exemplos de ISA são x86-64, ARM e RISC-V. Pela conveniência de ser o meu ponto central de estudos, focarei principalmente na ISA RISC-V.
Confusão entre ISA e Assembly
Na grande maioria dos casos concretos, o código produzido pelos compiladores está em linguagem Assembly, mas disso pode surgir a confusão de que o Assembly é a própria ISA. Podemos dizer que a ISA é a especificação, isto é, define o comportamento, enquanto o Assembly é a implementação, ou seja, a concretude da especificação, de certo modo. Recorrerei à simples analogia de que a ISA está para o Assembly do mesmo modo que a gramática está para a sintaxe, pois enquanto aquela define o idioma, suas regras e comportamentos, esta define as palavras escritas. Podem existir diversos tipos de sintaxe Assembly dentro de uma única ISA, como no caso do x86-64 (Intel) e do x86-64 (AT&T), que são sintaxes diferentes porém intercomunicáveis.
Implementações
Como dito anteriormente, focarei na ISA RISC-V. Observe a implementação abaixo:
li x6, 27 # Carrega 27 no registrador x6
add x7, x5, x6 # x7 = x5 + x6 (soma)
mv a0, x7 # Move resultado para o argumento
O código Assembly realiza a soma de dois números (15 + 27) através de quatro instruções básicas: Primeiro, as instruções li x5, 15
e li x6, 27
carregam os valores constantes nos registradores x5
e x6
, respectivamente. Em seguida, add x7, x5, x6
executa a soma propriamente dita, armazenando o resultado (42) no registrador x7
. Por fim, mv a0, x7
move o resultado para o registrador a0
, seguindo a convenção de chamada RISC-V, em que a0
é usado para retorno de valores. Isso demonstra que a ISA RISC-V possui alguns princípios fundamentais, como instruções simples e uniformes, operações load/store explícitas e uso eficiente do banco de 32 registradores, como toda boa ISA. A ISA RISC-V define exatamente como cada instrução deve ser codificada em binário e executada pelo hardware, criando uma interface clara entre ele e o software.