Como as linguagens influenciaram o design da CPU? [fechadas]

44

Estamos frequentemente informados de que o hardware não se importa com o idioma em que um programa é escrito, pois vê o código binário compilado, no entanto, isso não é toda a verdade. Por exemplo, considere o humilde Z80; as suas extensões para o conjunto de instruções 8080 incluem instruções como CPIR que é útil para pesquisar sequências de estilo C (terminadas em NULL), e. para executar strlen() . Os projetistas devem ter identificado que a execução de programas em C (em oposição a Pascal, onde o comprimento de uma string está no cabeçalho) era algo para o qual seu design provavelmente seria usado. Outro exemplo clássico é o Lisp Machine .

Que outros exemplos existem? Por exemplo. instruções, número e tipo de registradores , modos de endereçamento, que fazem um determinado processador favorecer as convenções de um idioma em particular? Estou particularmente interessado em revisões da mesma família.

    
por Gaius 30.07.2012 / 22:34
fonte

15 respostas

20

As respostas existentes se concentram na ISA muda. Existem outras alterações de hardware também. Por exemplo, o C ++ geralmente usa vtables para chamadas virtuais. Como começar com o Pentium M, a Intel um componente "preditor de ramificação indireta" que acelera as chamadas de função virtual.

    
por 31.07.2012 / 14:04
fonte
14

O conjunto de instruções Intel 8086 inclui uma variação de "ret" que adiciona um valor ao ponteiro da pilha depois de aparecer o endereço de retorno. Isto é útil para muitas implementações em Pascal, onde o chamador de uma função irá empurrar argumentos para a pilha antes de fazer uma chamada de função, e depois removê-los. Se uma rotina aceitaria, e. quatro bytes de parâmetros, poderia terminar com "RET 0004" para limpar a pilha. Na ausência de tal instrução, tal convenção de chamada provavelmente teria exigido que o código estourasse o endereço de retorno para um registrador, atualizasse o ponteiro da pilha e então pulasse para esse registro.

Curiosamente, a maior parte do código (incluindo rotinas de sistema operacional) no Macintosh original usava a convenção de chamada Pascal apesar da falta de uma instrução de facilitação no 68000. Usando essa convenção de chamada salvou de 2 a 4 bytes de código em um site de chamada comum, mas necessário um extra de 4-6 bytes de código no site de retorno de cada função que tomou parâmetros.

    
por 31.07.2012 / 01:21
fonte
10

Um exemplo é o MIPS, que tem add e addu para interceptar e ignorar o estouro, respectivamente. (Também sub e subu .) Precisava do primeiro tipo de instrução para linguagens como Ada (eu acho - eu nunca usei Ada na verdade) que lidam explicitamente com sobrecargas e o segundo tipo para linguagens como C que ignore overflows.

Se bem me lembro, a CPU real tem alguns circuitos adicionais na ALU para manter o controle de overflows. Se a única língua que as pessoas se importassem fosse C, não precisaria disso.

    
por 30.07.2012 / 22:45
fonte
8

A série Burroughs 5000 foi projetada para suportar eficientemente o ALGOL, e o iAPX-432 da Intel foi projetado para executar com eficiência a Ada. O Inmos Transputer tinha sua própria linguagem, Occam. Eu acho que o processador Parallax "Propeller" foi projetado para ser programado usando sua própria variante de BASIC.

Não é um idioma, mas o conjunto de instruções do VAX-11 possui uma única instrução para carregar um contexto de processo, que foi criado após uma solicitação da equipe de projeto do VMS. Não me lembro dos detalhes, mas o ISTR precisou de tantas instruções para implementar que colocou um limite superior no número de processos que eles poderiam programar.

    
por 30.07.2012 / 22:48
fonte
7

Uma coisa que ninguém parece ter mencionado até agora é que os avanços na otimização do compilador (onde a linguagem base é amplamente irrelevante) levaram a mudança dos conjuntos de instruções CISC (que foram projetados para serem codificados por humanos) para conjuntos de instruções RISC ( que foram amplamente projetados para serem codificados por compiladores.)

    
por 04.08.2012 / 17:52
fonte
5

A família Motorola 68000 introduziu alguns endereços de anúncios autoincrementos que fizeram a cópia de dados através da cpu muito eficiente e compacto.

[Exemplo atualizado]

este foi um código c ++ que influenciou 68000 assembler

while(someCondition)
    destination[destinationOffset++] = source[sourceOffset++]

implementado em assembler convencional (pseudocódigo, esqueci os comandos 68000 assembler)

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1)
    move (adressRegister2), akku
    increment(adressRegister1, 1)
    increment(adressRegister2, 1)
}

com o novo adressmode, tornou-se algo parecido com

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1++)
    move (adressRegister2++), akku
}

apenas duas instruções por loop, em vez de 4.

    
por 31.07.2012 / 13:27
fonte
5
O mainframe da série Z da IBM é o descendente do IBM 360 dos anos 60.

Havia várias instruções que foram colocadas especificamente para acelerar os programas COBOL e Fortran. O exemplo clássico é o BXLE - "Ramificação no índice baixo ou igual", que é a maior parte de um loop Fortran for ou um COBOL PERFORM VARYING x from 1 by 1 until x > n encapsulado em uma única instrução.

Existe também toda uma família de instruções decimais compactadas para suportar a aritmética decimal de ponto fixo comum em programas COBOL.

    
por 31.07.2012 / 08:23
fonte
3

Primeiros processadores Intel tinham os seguintes recursos, muitos deles agora obsoletos no modo de 64 bits:

  • instruções ENTER, LEAVE e RETnn [os primeiros manuais informaram explicitamente que foram introduzidos para linguagens estruturadas em bloco, por exemplo, Pascal, que suporta procedimentos aninhados]
  • instruções para acelerar a aritmética BCD (AAA, AAM, etc.); também suporte a BCD em x87
  • Instruções de JCXZ e LOOP para implementar loops contados
  • INTO, para gerar uma armadilha no estouro aritmético (por exemplo, em Ada)
  • XLAT para pesquisas de tabela
  • OBRIGADO para verificar limites de matriz

Sinalizador, encontrado no registro de status de muitas CPUs, existe para executar facilmente a aritmética assinada e não assinada.

O conjunto de instruções SSE 4.1 introduz instruções para o processamento de strings, contadas e terminadas com zero (PCMPESTR, etc.)

Além disso, posso imaginar que vários recursos no nível do sistema foram projetados para suportar segurança do código compilado (verificação de limite de segmento, portais de chamada com cópia de parâmetros, etc.)

    
por 01.08.2012 / 17:41
fonte
3

Alguns processadores ARM, principalmente aqueles em dispositivos móveis, incluem (d) a extensão Jazelle, que é o interpretador JVM de hardware; ele interpreta o bytecode de Java diretamente. A JVM que reconhece o Jazelle pode usar o hardware para acelerar a execução e eliminar muito do JIT, mas o fallback para o software VM ainda é assegurado se o bytecode não puder ser interpretado no chip.

Os processadores com essa unidade incluem a instrução BXJ, que coloca o processador em "modo Jazelle" especial ou, se a ativação da unidade falhar, ela é interpretada como uma instrução de ramificação normal. A unidade reutiliza os registradores ARM para manter o estado da JVM.

O sucessor da tecnologia Jazelle é ThumbEE

    
por 05.08.2012 / 10:23
fonte
2

Até onde eu sei, isso foi mais comum no passado.

Existe uma sessão de perguntas em que James Gosling disse que havia pessoas tentando fazer hardware que poderia lidar melhor com o bytecode da JVM, mas então essas pessoas descobririam uma maneira de fazer isso com o comum "genérico" intel x86 (talvez compilando o bytecode de alguma forma inteligente).

Ele mencionou que há vantagem em usar o chip popular genérico (como o da Intel) porque ele tem uma grande corporação jogando enormes somas de dinheiro no produto.

Vale a pena conferir o vídeo. Ele fala sobre isso no minuto 19 ou 20.

    
por 02.08.2012 / 21:36
fonte
2

Eu fiz uma pesquisa rápida de página e parece que ninguém mencionou a CPU desenvolvida especificamente para executar Forth . A A próxima linguagem de programação é baseada em pilha, compacta e usada em sistemas de controle.

    
por 09.08.2012 / 08:30
fonte
2

A CPU Intel iAPX foi projetada especificamente para idiomas OO. Não deu certo, no entanto.

The iAPX 432 (intel Advanced Processor architecture) was Intel's first 32-bit microprocessor design, introduced in 1981 as a set of three integrated circuits. It was intended to be Intel's major design for the 1980s, implementing many advanced multitasking and memory management features. The design was therefore referred to as a Micromainframe...

The iAPX 432 was "designed to be programmed entirely in high-level languages", with Ada being primary and it supported object-oriented programming and garbage collection directly in hardware and microcode. Direct support for various data structures was also intended to allow modern operating systems for the iAPX 432 to be implemented using far less program code than for ordinary processors. These properties and features resulted in a hardware and microcode design that was much more complex than most processors of the era, especially microprocessors.

Using the semiconductor technology of its day, Intel's engineers weren't able to translate the design into a very efficient first implementation. Along with the lack of optimization in a premature Ada compiler, this contributed to rather slow but expensive computer systems, performing typical benchmarks at roughly 1/4 the speed of the new 80286 chip at the same clock frequency (in early 1982).

This initial performance gap to the rather low profile and low priced 8086-line was probably the main reason why Intel's plan to replace the latter (later known as x86) with the iAPX 432 failed. Although engineers saw ways to improve a next generation design, the iAPX 432 Capability architecture had now started to be regarded more as an implementation overhead rather than as the simplifying support it was intended to be.

The iAPX 432 project was a commercial failure for Intel...

    
por 05.08.2012 / 05:29
fonte
1

O 68000 tinha o MOVEM mais adequado para empurrar vários registros para a pilha em uma única instrução, que é o que muitos idiomas esperavam.

Se você viu o MOVEM (MOVE Multiple) precedendo o JSR (Jump SubRoutine) em todo o código, então você geralmente sabia que estava lidando com o código C respeitado.

O MOVEM permitia o incremento automático do registro de destino, permitindo que cada uso continuasse empilhando no destino ou removendo da pilha no caso de decréscimo automático.

link

    
por 04.08.2012 / 19:09
fonte
1

A arquitetura AVR da Atmel é totalmente projetada desde o início para ser adequada para programação em C. Por exemplo, esta nota de inscrição elabora mais.

IMO está intimamente relacionado com rockets4kids'es excelente resposta , com PIC16-s cedo sendo desenvolvido para direta programação assembler (40 instruções no total), com famílias posteriores visando C.

    
por 27.09.2013 / 10:46
fonte
1

Quando o coprocessador numérico 8087 foi projetado, era bastante comum que os idiomas executassem toda a matemática de ponto flutuante usando o tipo de maior precisão e apenas arredondasse o resultado para menor precisão ao atribuí-lo a uma variável de menor precisão. No padrão C original, por exemplo, a sequência:

float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;

promoveria a e b a double , adicioná-los, promover c a double , adicioná-los e, em seguida, armazenar o resultado arredondado para float . Mesmo que em muitos casos teria sido mais rápido para um compilador gerar código que realizasse operações diretamente no tipo float , era mais simples ter um conjunto de rotinas de ponto flutuante que operariam apenas no tipo double , juntamente com rotinas para converter para / de float , do que ter conjuntos separados de rotinas para manipular operações em float e double . O 8087 foi projetado em torno dessa abordagem para a aritmética, executando todas as operações aritméticas usando um tipo de ponto flutuante de 80 bits [80 bits foi provavelmente escolhido porque:

  1. Em muitos processadores de 16 e 32 bits, é mais rápido trabalhar com uma mantissa de 64 bits e um expoente separado do que para trabalhar com valor, o que divide um byte entre a mantissa e o expoente.

  2. É muito difícil executar cálculos que sejam precisos para a precisão total dos tipos numéricos que um está usando; se alguém está tentando, e. computa algo como log10 (x), é mais fácil e mais rápido calcular um resultado que é preciso dentro de 100ulp de um tipo de 80 bits do que computar um resultado que seja preciso dentro de 1ulp de um tipo de 64 bits e arredondando o antigo resultado para precisão de 64 bits produzirá um valor de 64 bits que é mais preciso do que o último.

Infelizmente, versões futuras da linguagem mudaram a semântica de como os tipos de ponto flutuante deveriam funcionar; enquanto a semântica do 8087 teria sido muito boa se as linguagens tivessem suportado consistentemente, se as funções f1 (), f2 (), etc. retornassem o tipo float , muitos autores do compilador assumiriam a responsabilidade de tornar long double um alias para o tipo double de 64 bits em vez do tipo de 80 bits do compilador (e não fornece outros meios para criar variáveis de 80 bits) e avaliar arbitrariamente algo como:

double f = f1()*f2() - f3()*f4();

de uma das seguintes formas:

double f = (float)(f1()*f2()) - (extended_double)f3()*f4();
double f = (extended_double)f1()*f2() - (float)(f3()*f4());
double f = (float)(f1()*f2()) - (float)(f3()*f4());
double f = (extended_double)f1()*f2() - (extended_double)f3()*f4();

Note que se f3 e f4 retornarem os mesmos valores que f1 e f2, respectivamente, a expressão original deve retornar claramente zero, mas muitas das últimas expressões podem não retornar. Isso levou as pessoas a condenarem a "precisão extra" do 8087, embora a última formulação fosse geralmente superior à terceira e - com o código que usava apropriadamente o tipo duplo estendido - raramente fosse inferior.

Nos anos que se seguiram, a Intel respondeu à tendência da linguagem (IMHO infeliz) em forçar os resultados intermediários a serem arredondados para a precisão dos operandos projetando seus processadores posteriores de modo a favorecer esse comportamento, em detrimento do código que beneficiaria de usar maior precisão em cálculos intermediários.

    
por 03.11.2014 / 20:17
fonte