Máquinas de Pilha - Arquitecturas


6 - Máquinas de pilha, CISC e RISC

6.1 - Introdução

Uma comparação entre estas arquitecturas pode começar por uma divisão entre máquinas baseadas em registos e máquinas não baseadas em registos. O facto de disponibilizar explicitamente registos ao programador tem um enorme impacto na forma como o processador é desenhado. As máquinas de pilha com 0 operandos são extremamente simples.

Uma vantagem óbvia das máquinas de pilha é que a avaliação de expressões requer uma estrutura do tipo pilha. As máquinas baseadas em registos perdem algum tempo a emular uma máquina de pilha quando avaliam expressões. No entanto, as máquinas de pilha puras podem necessitar de mais operações do que as máquinas que disponibilizam 1 operando no formato das suas instruções, uma vez que não podem obter um valor e simultaneamente efectuar uma operação aritmética com esse valor. Por essa razão, máquinas como o RTX 32P que vimos anteriormente, usam instruções que desencadeiam múltiplas operações para compensar esse problema.

As máquinas que seguem a filosofia CISC possuem instruções bastante complexas, muitas vezes, mapeando directamente funções de linguagens de alto nível.

A filosofia RISC é fornecer um conjunto de instruções simples que são usadas pelo compilador para traduzir as instruções de linguagens de alto nível. Isto normalmente envolve sequências de leituras e escritas na memória e operações aritméticas para implementar cada uma das instruções das linguagens de alto nível.

As máquinas de pilha encontram-se entre estas duas filosofias. Têm primitivas muito simples que podem ser executadas num único ciclo de relógio, dentro do espírito da filosofia RISC. No entanto, os programas das máquinas de pilha usam intensivamente código específico da aplicação através de constantes chamadas a subrotinas, que podem ser vistas como um conjunto virtual de instruções que é usado pelos compiladores de linguagens de alto nível sem a necessidade de hardware complicado.

6.2 - Diferenças de arquitectura

A maior diferença entre máquinas de pilha e restantes tipos de máquinas é o uso dum modo de endereçamento com 0 operandos. Esta diferença quando combinada com um suporte a rápidas chamadas a subrotinas tornam as máquinas de pilha superiores às máquinas convencionais em aspectos como o tamanho do programa, complexidade do processador e do sistema, performance do sistema e consistência na execução do programa.

6.2.1 - Tamanho do programa 

	g5 = g1 - (g2 + 1) + g3 - (g4 * 2)

        Processador RISC     Máquina de pilha
                                push  g1
                                push  g2
        add   g2,#1,g5          inc   #1

        sub   g5,g1,g5          sub

                                push  g3
        add   g5,g3,g5          add

                                push  g4
        shl   g4,#1,temp        shl   #1

                                sub
        sub   temp,g5,g5        pop   g5

        20 bytes                10 bytes
        

A solução tradicional para programas de tamanho elevado é usar uma hierarquia de memória com diferentes características de capacidade, custo e tempo de acesso. Esta hierarquia compreende memória que varia da mais barata/maior/mais lenta para a mais cara/menor/mais rápida (exemplo: disco, memória, cache externa, cache interna). A memória mais rápida é simultaneamente a mais cara.

O problema traduz-se na capacidade de disponibilizar uma quantidade de memória suficiente para suportar o processador a um preço aceitável. Assim é necessário carregar a memória mais rápida com a maior quantidade possível de informação. Mas como esta memória tem uma capacidade limitada é fundamental, em especial em sistemas com recursos limitados, reduzir o tamanho dos programas a executar.

Tipicamente, as máquinas de pilha possuem programas de tamanho reduzido. Existem dois factores que levam ao reduzido tamanho dos programas nas máquinas de pilha: o formato das instruções e o suporte eficiente a frequentes chamadas a subrotinas.

Enquanto que as máquinas baseadas em registos necessitam de especificar em cada instrução uma operação e os seus operandos ou modo de endereçamento, as máquinas de pilha apenas indicam a operação a efectuar uma vez que os operandos são os valores de topo da pilha. As únicas instruções em que um operando está presente são as instruções de leitura e escrita em memória e a instrução que coloca um valor no topo da pilha.

Em máquinas CISC e RISC chamadas constantes a subrotinas destroem completamente a performance. Isto leva a que os programas sejam significativamente maiores. As máquinas de pilha são desenhadas para suportar eficientemente chamadas a subrotinas. Como os parâmetros estão presentes na pilha, o overhead das chamadas a procedimentos é mínimo, sendo que na maioria dos processadores de pilha as chamadas a subrotinas demoram 1 ciclo de relógio e os retornos são normalmente combinados com outras operações.

6.2.2 - Complexidade do sistema e do processador

Convém fazer a distinção entre complexidade do processador e complexidade do sistema. A complexidade do processador é a quantidade de lógica (área do chip, número de transístores, etc.) no núcleo do processador. A complexidade do sistema considera o processador inserido num sistema plenamente funcional que contém hardware de suporte, uma hierarquia de memória e software. 

A complexidade das máquinas CISC resulta da tentativa de reduzir a distância entre a semântica das linguagens de alto nível e a máquina para manter os programas relativamente pequenos. No entanto, isto pode levar a que quase toda a área disponível do chip seja usada para controlo. Esta melhoria da interface entre software e hardware significa que os compiladores não necessitam ser excessivamente complexos e que podem ser reutilizados entre várias máquinas diferentes da mesma família.

O conceito por detrás das máquinas RISC é tornar o processador rápido reduzindo a sua complexidade, recorrendo a instruções simples, cada uma delas efectuando pouco trabalho mas demorando pouco tempo a fazê-lo. Mas, esta redução da complexidade exige uma grande quantidade de registos, memória de duas entradas para permitir o acesso simultâneo a diferentes endereços, maior largura de banda no acesso à memória e uma pipeline interna, exigindo capacidades extra do hardware ou do compilador. É necessário prestar especial atenção, e consequentemente hardware extra, à ocorrência de interrupções para assegurar o armazenamento e posterior leitura do estado da pipeline. Certas estratégias de implementação RISC exigem bastante aos compiladores, tais como o escalonamento da pipeline, preenchimento do branch delay slot e a gestão dos registos.

As máquinas de pilha são extraordinariamente simples. Obtêm esta simplicidade não pela redução do número de instruções mas limitando os dados que cada instrução pode manipular aos elementos de topo da pilha. As instruções são assim bastante compactas, uma vez que especificam apenas a operação a realizar. A memória pode ter apenas uma entrada uma vez que apenas um elemento é retirado ou colocado na pilha por cada ciclo de relógio, já que os dois elementos de topo se encontram normalmente em registos. O resultado desta complexidade reduzida é que as máquinas de pilha têm mais espaço no chip para memória ou outro hardware que vá de encontro às necessidades do sistema. Para certos programas é mesmo possível que toda a memória necessária esteja no chip, eliminando a necessidade de um controlo complexo da cache. Os compiladores para as máquinas de pilha são também simples porque as instruções são muito consistentes no formato e na selecção dos operandos.

6.2.3 - Consistência na execução dos programas

As máquinas CISC e RISC possuem um conjunto de técnicas, como pipeline, previsão de saltos, memórias cache, scoreboarding, etc., que proporcionam uma elevada performance ao longo do tempo mas não garantem uma elevada performance num determinado instante. No entanto, uma sequência de eventos externos ou valores internos pode significar atrasos intoleráveis em aplicações de tempo real.

Como as máquinas de pilha não usam nenhuma destas técnicas a sua performance é consistente ao longo do tempo, aspecto fundamental na área de aplicações de tempo real.