Wie haben Sprachen das CPU-Design beeinflusst? [geschlossen]


44

Wir werden oft darauf hingewiesen, dass es der Hardware egal ist, in welcher Sprache ein Programm geschrieben ist, da nur der kompilierte Binärcode angezeigt wird. Dies ist jedoch nicht die ganze Wahrheit. Betrachten Sie zum Beispiel den bescheidenen Z80; Zu den Erweiterungen des 8080-Befehlssatzes gehören Befehle wie CPIR, die zum Scannen von C-Zeichenfolgen (mit NULL-Abschluss) nützlich sind, z strlen(). B. zum Ausführen . Die Designer müssen festgestellt haben, dass das Ausführen von C-Programmen (im Gegensatz zu Pascal, bei dem die Länge einer Zeichenfolge im Header enthalten ist) etwas war, wofür ihr Design wahrscheinlich verwendet wurde. Ein weiteres klassisches Beispiel ist die Lisp-Maschine .

Welche anderen Beispiele gibt es? ZB Befehle, Anzahl und Art der Register , Adressierungsmodi, die einen bestimmten Prozessor dazu bringen, die Konventionen einer bestimmten Sprache zu bevorzugen? Ich interessiere mich besonders für Revisionen derselben Familie.


3
Vergessen Sie nicht, dass der Z-80 auch den LDIR-Befehl hatte, der beim Kopieren von Strings sehr nützlich ist, wenn Sie die Länge kennen (wie in Pascal, wo die Länge im Header gespeichert wurde).
TMN

27
1. Der Z-80 wurde 1975 entwickelt, als Unix und C drei Jahre vor der ersten Ausgabe von K & R auf einigen Computern ein undurchsichtiges Betriebssystem und eine undurchsichtige Sprache waren. 2. Pascal schreibt nichts vor, dass die Länge der Zeichenkette "in einem Header" sein muss. 3. Zeichenfolgen in CP / M, dem zu diesem Zeitpunkt wichtigsten Mikrocomputer-Betriebssystem, wurden mit dem Zeichen "$" und nicht mit "\ 0" abgeschlossen. CPIR kann nach beliebigen Zeichen suchen. 4. CPIR wird mit CPDR (Rückwärtssuche) sowie anderen -IR- und -DR-Anweisungen abgeglichen. Fazit: CPIR hat nichts mit der Programmiersprache C zu tun. Es ist nur eine Bytesuchanweisung.
Librik

4
Das größte (und für die Hardware-Designer ärgerlichste) der von C erzwungenen Dinge ist die Adressierung von Bytes. CPUs wären ohne diesen Gräuel einfacher und schneller gewesen.
SK-logic

1
@ SK-Logik: Obwohl der POSIX-Standard eine Byteadressierung erfordert, ist dies beim C-Standard nicht der Fall. Bei jeder Implementierung mit sizeof(int)1 muss der Typ charsigniert sein (da intalle Werte des Typs enthalten sein müssen char). Ich habe für eine Maschine geschriebenen Code , wo charund intsind beide 16-Bit - Integer mit Vorzeichen; Die größten Schwierigkeiten bestehen darin, dass man keine Gewerkschaften für die Typkonvertierung verwenden kann und eine effiziente Speicherung einer großen Anzahl von Bytes manuelles Packen und Entpacken erfordert. Diese Probleme sind geringfügig im Vergleich zu der Möglichkeit in C, dass sizeof (int) == sizeof (long), da ...
Supercat

2
... das heißt, es gibt keinen Standardtyp, der garantiert die Differenz zwischen zwei unsigned intWerten hält. C99 verbesserte diese Situation, aber vor C99 gab es keine Möglichkeit, einen potenziell negativen Wert in einem einzigen Schritt mit einem Wert vom Typ zu vergleichen unsigned int(man müsste testen, ob die Zahl negativ war, bevor der Vergleich durchgeführt wird).
Superkatze

Antworten:


20

Die vorhandenen Antworten konzentrieren sich auf ISA- Änderungen. Es gibt auch andere Hardware-Änderungen. Beispielsweise verwendet C ++ üblicherweise vtables für virtuelle Aufrufe. Ab dem Pentium M verfügt Intel über eine "indirekte Verzweigungsvorhersage" -Komponente, die virtuelle Funktionsaufrufe beschleunigt.


6
Und die Berkeley-RISC-Architektur enthielt das Konzept einer "Registerdatei", sodass, anstatt dass Funktionen auf den Stapel "verschüttet" werden, jeder Funktion ein Block von 8 Registern zugewiesen wurde. Dies hat den objektorientierten Code erheblich beschleunigt, da er in der Regel aus vielen Methodenaufrufen für kurze Methoden besteht.
TMN

1
Dies ist kein gültiges Beispiel. Das Design "Tabelle der Funktionszeiger" wird auch in vielen dynamischen Verknüpfungsszenarien verwendet, z. B. durch DLL-Import und -Export unter Windows und auch in C-Programmen. Sie könnten zwar argumentieren, dass der Prozessor für eine bestimmte Verwendung optimiert ist, er ist jedoch nicht sprachspezifisch.
DeadMG

@DeadMG: Andere Fälle haben davon profitiert, das stimmt. Aber bis C ++ populär wurde, wurden die CPU-Designs nicht beeinflusst . Und das war die Frage. In ähnlicher Weise hat TMN einen Punkt über Registerdateien. Die Versammlung hatte kein so klares Funktionskonzept. Die Funktionen, wie wir sie heute allgemein verstehen, stammen aus Algol 60, und daher können wir sagen, dass Algol 60 das Design der CPU-Registerdatei beeinflusst hat.
MSalters

14

Der Intel 8086-Befehlssatz enthält eine Variation von "ret", die dem Stapelzeiger einen Wert hinzufügt, nachdem die Rücksprungadresse abgesetzt wurde. Dies ist für viele Pascal-Implementierungen nützlich, bei denen der Aufrufer einer Funktion Argumente vor dem Aufrufen einer Funktion auf den Stapel schiebt und sie anschließend abruft. Wenn eine Routine beispielsweise Parameter im Wert von vier Bytes akzeptiert, könnte sie mit "RET 0004" enden, um den Stapel zu bereinigen. Ohne einen solchen Befehl hätte eine solche Aufrufkonvention wahrscheinlich erforderlich gemacht, dass der Code die Rücksprungadresse in ein Register eingibt, den Stapelzeiger aktualisiert und dann zu diesem Register springt.

Interessanterweise verwendete der größte Teil des Codes (einschließlich der OS-Routinen) auf dem ursprünglichen Macintosh die Pascal-Aufrufkonvention, obwohl es im 68000 keine Anweisungen zur Erleichterung gab. Die Verwendung dieser Aufrufkonvention sparte 2-4 Byte Code an einem typischen Aufrufstandort, erforderte jedoch zusätzliche 4-6 Byte Code an der Rückgabeseite jeder Funktion, die Parameter verwendet hat.


Es gibt auch ein ENTERGegenstück dazu RET n...
herby

1
@herby: Ich glaube nicht, dass ENTERes im Original 8086 existiert hat; es kam mit späteren Prozessoren. Es bringt jedoch einen interessanten Punkt auf den Punkt: Die BP-basierten Adressierungsmodi sind klar auf die Verwendung von gestapelten Parametern und Lokalen ausgelegt, auf die über den Rahmenzeiger zugegriffen wird. Ich finde diese Konvention in vielerlei Hinsicht interessant, insbesondere angesichts der Tatsache, dass (1) reiner Assemblersprachencode eher Werte in Registern als im Stapel verwendet, aber (2) die Vorteile der [BP + nn] -Adressierung gegenüber [SP +] nn] Adressierung ist für Assembler-Programme, die auf den Stack zugreifen,
wichtiger

... für handgeschriebenen Assembler-Code. Ein Compiler weiß im Allgemeinen für jede generierte Anweisung, wie SP und BP verglichen werden. Wenn SP beispielsweise BP-8 ist, ist es für den Compiler nicht einfacher, [BP + 12] als [SP + 20] zu adressieren. Wenn der Compiler bei einer Neukompilierung ein weiteres PUSH / POP um einen Codeblock hinzufügen muss, kann er SP-basierte Offsets entsprechend anpassen. Andererseits würde das Hinzufügen eines PUSH / POP bei einer handschriftlichen Assemblierung mit größerer Wahrscheinlichkeit das Ändern des Codes zwischen ihnen erfordern. Frame-Zeiger sind daher hauptsächlich ein Vorteil für kombinierten High-Level- / ASM-Code.
Superkatze

Vielleicht ist eine Möglichkeit, Code ohne Neukompilierung wiederzuverwenden, auch für die BP-Adressierung von geringem Nutzen. Und Gott weiß, ob BP-Adressierungsanweisungen in der Schaltung nicht schneller sind als SP-Adressierungsanweisungen, da BP-Adressierung eine Art Standard ist ...
herby

3
@herby: Eigentlich vermute ich, dass ein großer Teil des Grundes, warum Compiler im Allgemeinen Frame-Zeiger verwenden, viel mit dem Debuggen zu tun hat. Um ein Programm zu debuggen, das keine solche Konvention verwendet, müsste der Compiler eine Datei generieren - und der Debugger verwenden -, in der der SP-BP-Offset für jede Anweisung aufgeführt ist. Solche detaillierten Metadaten sind heutzutage weit verbreitet (und ein wesentlicher Teil dessen, was Sprachen, die mit Müll gesammelt wurden, praktisch sind), aber die Menge an RAM, die dafür benötigt wird, wäre vor 30 Jahren inakzeptabel gewesen.
Superkatze

10

Ein Beispiel hierfür ist MIPS, die beide aufweist addund adduzum Auffangen und Überlauf zu ignorieren ist. (Auch subund subu.) Es brauchte die erste Art von Anweisungen für Sprachen wie Ada (ich glaube - ich habe Ada allerdings noch nie benutzt), die sich explizit mit Überläufen befassen, und die zweite Art für Sprachen wie C, die Überläufe ignorieren.

Wenn ich mich richtig erinnere, hat die eigentliche CPU einige zusätzliche Schaltkreise in der ALU, um Überläufe zu verfolgen. Wenn die einzige Sprache, um die sich die Leute kümmerten, C wäre, würde es das nicht brauchen.


Nicht sicher, ob ein Zusammenhang besteht, aber diese Anweisungen sind wahrscheinlich auch in anderen Situationen nützlich, z. B. bei der sicheren Speicherzuweisung, dh wenn Sie nmemb*size+offsetBytes zuweisen und sicherstellen müssen, dass kein Überlauf auftritt.
NikiC

@NikC: Ich dachte, dass die Anweisungen adduund subu(diejenigen, die nicht auf Überläufe prüfen) diejenigen sind, die hinzugefügt wurden, um C glücklich zu machen. Natürlich weiß ich es nicht genau - wir haben es nur vage in der Vorlesung behandelt und ich bin mit Sicherheit kein Experte für Architektur: P.
Tikhon Jelvis

Oh ja, ich dachte umgekehrt, sorry: /
NikiC

8

Die Burroughs 5000-Serie wurde entwickelt, um ALGOL effizient zu unterstützen, und Intels iAPX-432 wurde entwickelt, um Ada effizient auszuführen. Der Inmos Transputer hatte eine eigene Sprache, Occam. Ich denke, der Parallax "Propeller" -Prozessor wurde so entworfen, dass er mit einer eigenen BASIC-Variante programmiert werden kann.

Es ist keine Sprache, aber der VAX-11-Befehlssatz enthält einen einzelnen Befehl zum Laden eines Prozesskontexts, der auf Anfrage des VMS-Designteams erstellt wurde. Ich erinnere mich nicht an die Details, aber ISTR brauchte so viele Anweisungen, um es zu implementieren, dass die Anzahl der Prozesse, die geplant werden konnten, ernsthaft begrenzt wurde.


Was macht diese Designs besonders geeignet? ZB von welcher iAPX-Funktion profitiert Ada besonders?
Gaius

ISTR, dass das Ada-Ziel von iAPX-432 mehr versucht hat, ein fehlgeschlagenes Design zu retten, indem es an etwas angehängt wurde, das noch höhere Erwartungen hat als alles andere.
AProgrammer

@AProgrammer: Ich bin mir ziemlich sicher, dass der iAPX-432 von Anfang an für die Verwendung von Ada entwickelt wurde. Ich erinnere mich sogar an einige Gerüchte, wonach Intel den Befehlssatz nicht veröffentlichen würde, um die Assembler-Programmierung zu unterbinden und die Leute zu zwingen, Ada für alles zu verwenden.
TMN

1
@TMN, das 432-Projekt von Intel wurde 1975 gestartet und 1981 eingeführt (Wikipedia). Ironman (endgültige Anforderungen für Ada) wurde im Januar 1977 veröffentlicht, und Green wurde im Mai 1979 ausgewählt, modifiziert und das endgültige Ergebnis im Juli 1980 als militärischer Standard veröffentlicht der Anfang, Ada zu benutzen. (Es handelt sich um einen späten und typischen Prozessor zum Schließen der semantischen Lücke mit den üblichen Nachteilen zu einem Zeitpunkt, als die Suche nach Alternativen begann. Die Vermarktung als Ada-Prozessor war ein Versuch, ein fehlgeschlagenes Design zu retten - ISTR, das nur von Intel verwendet wurde )
Programmierer

1
@AProgrammer: Hmmm, sieht so aus, als hättest du recht. Ich bin auf dieses Papier des Hauptarchitekten des 432 gestoßen, und in der Zusammenfassung sagt er: "Diese enge Übereinstimmung von Architektur und Sprache ist nicht eingetreten, weil der 432 für die Ausführung von Ada konzipiert wurde - das war es nicht." Ich muss mein altes Buch 432 ausgraben und sehen, was darin steht.
TMN

8

Bisher scheint noch niemand etwas erwähnt zu haben: Fortschritte bei der Compileroptimierung (bei der die Basissprache weitgehend irrelevant ist) haben die Verschiebung von CISC-Befehlssätzen (die größtenteils für den menschlichen Gebrauch konzipiert wurden) zu RISC-Befehlssätzen (die größtenteils von Menschen erstellt wurden) ausgelöst entworfen, um von Compilern codiert zu werden.)


5

Die Motorola 68000-Familie hat einen automatischen Adressierungsmodus eingeführt , der das Kopieren von Daten über die CPU sehr effizient und kompakt macht.

[Aktualisiertes Beispiel]

Dies war ein C ++ - Code, der 68000 Assembler beeinflusste

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

implementiert in herkömmlichen Assembler (Pseudocode, ich habe die 68000 Assembler-Befehle vergessen)

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

mit dem neuen adressmodus wurde es zu etwas ähnlichem

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

nur zwei Anweisungen pro Schleife anstelle von 4.


1
Wie wurde dies durch die Konventionen einer bestimmten Sprache beeinflusst?
Gaius

siehe aktualisiertes Beispiel
k3b

Ah, erinnert mich an die DBxx-Schleifenoptimierung in der 68010.
Gaius

7
Eigentlich denke ich, dass Sie das rückwärts haben. Die automatische [inkrementelle] Adressierung war Teil des PDP-11-Befehlssatzes, der wahrscheinlich das Design von C.
TMN

5

Der IBM Mainframe der Z-Serie ist der Nachfolger des IBM 360 aus den 1960er-Jahren.

Es gab mehrere Anweisungen, die speziell für die Beschleunigung von COBOL- und Fortran-Programmen erstellt wurden. Das klassische Beispiel ist BXLE- "Branch on Index Low oder Equal", bei dem es sich zumeist um eine Fortran- forSchleife oder ein COBOL handelt, PERFORM VARYING x from 1 by 1 until x > ndas in einem einzelnen Befehl verkapselt ist.

Es gibt auch eine ganze Familie gepackter Dezimalbefehle zur Unterstützung von Festkomma-Dezimalarithmetik, wie sie in COBOL-Programmen üblich sind.


Ich denke du meinst Nachkomme .
Clockwork-Muse

@ X-Zero - Hoppla! Am frühen Morgen, nicht genug Koffein im System usw.
James Anderson

1
Interessanter ist der Block-Repeat-Befehl des TI 32050 DSP. Sein Operand ist die Adresse des Befehls, der auf den letzten in der Schleife folgt. Das Laden eines Schleifenzählungsregisters und das anschließende Ausführen des Blockwiederholungsbefehls bewirkt, dass Befehle bis zum (jedoch nicht einschließlich) des Ziels die angegebene Anzahl von Malen wiederholt werden. Sehr stark an eine FORTRAN- DOSchleife erinnernd .
Superkatze

@supercat Jeder DSP, der diesen Namen verdient, enthält drei Funktionen: eine Null-Overhead-Schleife, eine Multiplikations-Akkumulation mit einem Befehl und eine Art bitumgekehrter Adressierungsmodus. Fast jeder dem Menschen bekannte DSP-Algorithmus verwendet Schleifen. Die beiden häufigsten Algorithmen sind FIR-Filter, eine Schleife um eine Multiplikationsakkumulation, und FFT, für die die bitumgekehrte Adressierung von entscheidender Bedeutung ist. Viele DSPs enthalten eine Radix-2-FFT-Butterfly-Operation mit einem Befehl oder eine doppelte Multiplikation / Addition, mit der ein Butterfly mit einem Befehl erstellt werden kann.
John R. Strohm

@ JohnR.Strohm: Jeder DSP, den ich gesehen habe, enthält eine Wiederholungs-Multiplikations-Akkumulation, aber nicht alle enthalten allgemeinere Null-Overhead-Schleifen. Eigentlich bin ich mir nicht ganz sicher, warum solche Schleifen nur als "DSP" -Funktion angesehen werden sollten, da sie auch in vielen "herkömmlichen Prozessor" -Codes nützlich wären.
Superkatze

3

Frühe Intel-CPUs hatten die folgenden Funktionen, von denen viele im 64-Bit-Modus veraltet waren:

  • ENTER-, LEAVE- und RET-nn-Anweisungen [frühere Handbücher, die ausdrücklich erwähnt wurden, wurden für blockstrukturierte Sprachen eingeführt, z. B. Pascal, das verschachtelte Prozeduren unterstützt]
  • Anweisungen zur Beschleunigung der BCD-Arithmetik (AAA, AAM usw.); auch BCD-Unterstützung in x87
  • JCXZ- und LOOP-Anweisungen zum Implementieren gezählter Schleifen
  • INTO, zum Erzeugen einer Falle bei arithmetischem Überlauf (zB in Ada)
  • XLAT für Tabellensuchen
  • BOUND zum Überprüfen von Array-Grenzen

Das Vorzeichen-Flag, das sich im Statusregister vieler CPUs befindet, dient zur einfachen Ausführung von Arithmetik mit und ohne Vorzeichen.

Der SSE 4.1-Befehlssatz enthält Anweisungen für die Zeichenfolgenverarbeitung, sowohl gezählt als auch nullterminiert (PCMPESTR usw.).

Ich könnte mir auch vorstellen, dass eine Reihe von Funktionen auf Systemebene entwickelt wurden, um die Sicherheit von kompiliertem Code zu unterstützen (Segmentlimitprüfung, Call-Gates beim Kopieren von Parametern usw.).


3

Einige ARM-Prozessoren, hauptsächlich solche in Mobilgeräten, umfassen (d) Jazelle-Erweiterung, bei der es sich um einen Hardware-JVM-Interpreter handelt; es interpretiert Java-Bytecode direkt. Jazelle-fähige JVM können die Hardware verwenden, um die Ausführung zu beschleunigen und einen Großteil von JIT zu eliminieren. Ein Rückgriff auf Software-VM ist jedoch weiterhin gewährleistet, wenn der Bytecode nicht auf dem Chip interpretiert werden kann.

Prozessoren mit einer solchen Einheit enthalten einen BXJ-Befehl, der den Prozessor in einen speziellen "Jazelle-Modus" versetzt, oder wenn die Aktivierung der Einheit fehlgeschlagen ist, wird sie nur als normaler Verzweigungsbefehl interpretiert. Das Gerät verwendet ARM-Register erneut, um den JVM-Status zu halten.

Der Nachfolger der Jazelle-Technologie ist ThumbEE


2

Soweit ich weiß, war dies in der Vergangenheit üblicher.

Es gibt eine Reihe von Fragen, in denen James Gosling sagte, dass es Leute gab, die versuchten, Hardware zu entwickeln, die besser mit JVM - Bytecode umgehen kann, aber dann würden diese Leute einen Weg finden, dies mit allgemeinem Intel x86 zu tun (möglicherweise beim Kompilieren des Bytecode auf eine clevere Art und Weise).

Er erwähnte, dass die Verwendung des beliebten generischen Chips (wie Intel) von Vorteil ist, da ein großes Unternehmen riesige Summen auf das Produkt wirft.

Das Video ist einen Blick wert. Er spricht in Minute 19 oder 20 darüber.



2

Die Intel iAPX- CPU wurde speziell für OO-Sprachen entwickelt. Hat aber nicht ganz geklappt.

Der iAPX 432 ( Intel Advanced Processor Architecture ) war der erste 32-Bit-Mikroprozessor von Intel, der 1981 als Satz von drei integrierten Schaltkreisen eingeführt wurde. Es sollte Intels wichtigstes Design für die 1980er Jahre sein und viele fortschrittliche Funktionen für Multitasking und Speicherverwaltung implementieren. Das Design wurde daher als Micromainframe bezeichnet ...

Die iAPX 432 wurde „ die ganz in Hochsprachen programmiert zu werden“ , mit Ada wobei primäre und es unterstützt die objektorientierte Programmierung und die Garbage Collection direkt in Hardware und Mikrocode . Die direkte Unterstützung verschiedener Datenstrukturen sollte es auch ermöglichen, moderne Betriebssysteme für den iAPX 432 mit weitaus weniger Programmcode als für normale Prozessoren zu implementieren. Diese Eigenschaften und Merkmale führten zu einem Hardware- und Mikrocode-Design, das viel komplexer war als die meisten Prozessoren der Ära, insbesondere Mikroprozessoren.

Mit der damaligen Halbleitertechnologie konnten die Ingenieure von Intel das Design nicht in eine sehr effiziente erste Implementierung umsetzen. Zusammen mit dem Mangel an Optimierung in einem vorzeitigen Ada-Compiler trug dies zu relativ langsamen, aber teuren Computersystemen bei, die typische Benchmarks mit etwa 1/4 der Geschwindigkeit des neuen 80286-Chips bei derselben Taktfrequenz (Anfang 1982) durchführten.

Diese anfängliche Leistungslücke zur eher unauffälligen und preisgünstigen 8086-Linie war wahrscheinlich der Hauptgrund, warum Intels Plan, die letztere (später als x86 bezeichnet) durch die iAPX 432 zu ersetzen, gescheitert ist. Obwohl die Ingenieure Möglichkeiten zur Verbesserung eines Designs der nächsten Generation sahen, wurde die iAPX 432 Capability-Architektur nun eher als Implementierungsaufwand denn als die beabsichtigte vereinfachende Unterstützung angesehen.

Das iAPX 432-Projekt war ein kommerzieller Misserfolg für Intel ...


Wenn man die Zeitung liest, klingt es so, als ob viele Aspekte des Designs in objektorientierten Frameworks nützlich sein könnten, wie sie heute populär sind. Eine Architektur, die eine Kombination aus einer 32-Bit-Objekt-ID und einem 32-Bit-Versatz verwendet, bietet in vielen Fällen eine bessere Caching-Leistung als eine Architektur, bei der die Objekt-ID alle 64 Bit beträgt (in den meisten Fällen würde eine Anwendung Milliarden von Objekten verwenden) Besser bedient werden durch mehr, größere, eine, die Milliarden von Bytes in einem Objekt speichern würde, wäre besser bedient, wenn man diese in kleinere Objekte unterteilt.
Supercat

1

Der 68000 verfügte über MOVEM, das am besten geeignet war, mehrere Register in einem einzigen Befehl auf den Stapel zu schieben, was viele Sprachen erwarteten.

Wenn Sie im gesamten Code MOVEM (MOVE Multiple) vor JSR (Jump SubRoutine) sahen, wussten Sie im Allgemeinen, dass es sich um C-konformen Code handelte.

MOVEM ermöglichte das automatische Inkrementieren des Zielregisters, sodass jede Verwendung das Stapeln des Ziels fortsetzen oder im Fall eines automatischen Dekrements aus dem Stapel entfernen konnte.

http://68k.hax.com/MOVEM


1

Die AVR-Architektur von Atmel wurde von Grund auf so konzipiert, dass sie für die Programmierung in C geeignet ist. In diesem Anwendungsbericht wird beispielsweise näher darauf eingegangen.

IMO ist dies eng mit rockets4kids'es ausgezeichneter Zusammenhang Antwort , mit dem frühen PIC16-s für die direkten Assembler - Programmierung (40 Befehle insgesamt) entwickelt, mit späteren Familien C. Targeting


1

Bei der Entwicklung des numerischen 8087-Coprozessors war es für Sprachen üblich, alle Gleitkomma-Berechnungen mit dem Typ mit der höchsten Genauigkeit durchzuführen und das Ergebnis nur auf eine niedrigere Genauigkeit abzurunden, wenn es einer Variablen mit niedrigerer Genauigkeit zugewiesen wurde. In der ursprünglichen C-Norm ist zum Beispiel die Reihenfolge:

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

würde fördern aund bzu double, sie hinzufügen, fördern czu double, es hinzufügen und dann das Ergebnis speichern, auf das gerundet wird float. Obwohl es in vielen Fällen für einen Compiler schneller gewesen wäre, Code zu generieren, der Operationen direkt auf dem Typ ausführt float, war es einfacher, eine Reihe von Gleitkommaroutinen zu haben, die nur auf dem Typ funktionieren würden double, zusammen mit Routinen zum Konvertieren in / von float, als separate Sätze von Routinen zu haben, um Operationen auf floatund zu handhaben double. Der 8087 wurde um diesen Ansatz für die Arithmetik entwickelt und führt alle arithmetischen Operationen mit einem 80-Bit-Gleitkomma-Typ aus. [80 Bits wurden wahrscheinlich gewählt, weil:

  1. Auf vielen 16- und 32-Bit-Prozessoren ist es schneller, mit einer 64-Bit-Mantisse und einem separaten Exponenten zu arbeiten, als mit einem Wert, der ein Byte zwischen Mantisse und Exponenten teilt.

  2. Es ist sehr schwierig, Berechnungen durchzuführen, die auf die volle Genauigkeit der verwendeten numerischen Typen genau sind. Wenn man zum Beispiel versucht, etwas wie log10 (x) zu berechnen, ist es einfacher und schneller, ein Ergebnis zu berechnen, das bis auf 100 μs eines 80-Bit-Typs genau ist, als ein Ergebnis, das bis auf 1 μs eines 64-Bit-Typs genau ist Wenn Sie das erstere Ergebnis auf 64-Bit-Genauigkeit runden, erhalten Sie einen 64-Bit-Wert, der genauer ist als der letztere.

Leider haben zukünftige Versionen der Sprache die Semantik der Funktionsweise von Gleitkommatypen geändert. Während die 8087-Semantik sehr schön gewesen wäre, wenn Sprachen sie konsistent unterstützt hätten, wenn die Funktionen f1 (), f2 () usw. den Typ zurückgegeben hätten float, würden viele Compiler-Autoren es auf sich nehmen, long doubleeinen Alias ​​für den 64-Bit-Doppeltyp zu erstellen anstelle des 80-Bit-Typs des Compilers (und bieten keine andere Möglichkeit zum Erstellen von 80-Bit-Variablen) und zum willkürlichen Auswerten von:

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

auf eine der folgenden Arten:

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();

Beachten Sie, dass, wenn f3 und f4 dieselben Werte wie f1 bzw. f2 zurückgeben, der ursprüngliche Ausdruck eindeutig Null zurückgeben sollte, viele der letzteren Ausdrücke jedoch möglicherweise nicht. Dies führte dazu, dass die Leute die "zusätzliche Präzision" des 8087 verurteilten, obwohl die letzte Formulierung im Allgemeinen der dritten überlegen war und - mit Code, der den erweiterten Doppeltyp angemessen verwendete - selten minderwertig war.

In den vergangenen Jahren hat Intel auf den (unglücklichen) Trend der Sprache reagiert, Zwischenergebnisse auf die Genauigkeit der Operanden abzurunden, indem die späteren Prozessoren so entworfen wurden, dass sie dieses Verhalten begünstigen, was sich nachteilig auf den Code auswirkt, der von der Verwendung höherer Werte profitiert Präzision bei Zwischenberechnungen.


Beachten Sie, dass Sie in diesem Beitrag bereits eine Antwort ( oben ) erhalten haben. Sind es Antworten, die zu einer zusammengefasst werden könnten / sollten?

@MichaelT: Ich glaube nicht - einer behandelt das Stack-Design und der andere die Gleitkommasemantik.
Supercat

Nur um sicher zu gehen. Persönlich glaube ich, dass es möglich wäre, eine stärkere Antwort zu geben (indem ich Überschriften verwende, um die Abschnitte zu trennen), aber das ist meine Meinung dazu. Möglicherweise möchten Sie weiterhin Header verwenden, um oben klar zu identifizieren, an welchen Stellen die einzelnen Antwortteile gerichtet sind ( ## How the stack changed the processorund ## How floating point changed the processor), damit die Benutzer beim Lesen die richtige Einstellung erhalten und weniger wahrscheinlich glauben, dass Sie bei der Beantwortung oder Neuveröffentlichung der Fragen abwesend waren gleiche (r ähnliche) Antworten.

@MichaelT: Die beiden Antworten sind so unzusammenhängend, dass ich denke, dass über sie separat abgestimmt werden sollte. Obwohl der 80486 die zuvor vom 8087/80287/80387 ausgeführten Funktionen übernahm, wurden der 8086 und der 8087 als separate Chips mit nahezu unabhängigen Architekturen konzipiert. Obwohl beide Code aus einem gemeinsamen Befehlsstrom ausführten, behandelten die 8086 bestimmte Byte-Sequenzen als Anforderungen zum Generieren von Lese- / Schreibanforderungen für Adressen, während sie den Datenbus ignorierten, und die 8087 ignorierten alles andere, was vor sich ging.
Supercat
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.