よく言われます コンパイルされたバイナリコードしか認識しないため、ハードウェアはプログラムがどの言語で書かれているかを気にしませんが、これは完全な真実ではありません。たとえば、控えめなZ80について考えてみます。 8080命令セットへの拡張には、Cスタイル(NULLで終了する)文字列のスキャンに役立つCPIRなどの命令が含まれています。 strlen()
を実行します。設計者は、(文字列の長さがヘッダーにあるPascalとは対照的に)Cプログラムの実行が、設計で使用される可能性が高いものであることを認識している必要があります。別の古典的な例は LISP Machine です。
他にどのような例がありますか?例えば。命令、 レジスタの数とタイプ 、アドレス指定モード、特定のプロセッサに特定の言語の規則を優先させるか?私は同じ家族の改定に特に興味があります。
既存の回答は [〜#〜] isa [〜#〜] の変更に重点を置いています。他のハードウェアの変更もあります。たとえば、C++は通常、仮想呼び出しにvtableを使用します。 Pentium Mで始まる 、Intelには、仮想関数呼び出しを高速化する「間接分岐予測」コンポーネントがあります。
Intel 8086命令セットには、スタックポインタに値を追加する「ret」のバリエーションが含まれています 後 戻りアドレスをポップします。これは、関数の呼び出し元が関数呼び出しを行う前に引数をスタックにプッシュし、後でそれらをポップする多くのPascal実装に役立ちます。ルーチンが受け入れるなら4バイトのパラメータの場合、スタックをクリーンアップするために「RET 0004」で終わる可能性があります。そのような命令がなければ、そのような呼び出し規約では、コードが戻りアドレスをレジスターにポップし、スタックポインターを更新してから、そのレジスターにジャンプする必要があったでしょう。
興味深いことに、元のMacintoshのほとんどのコード(OSルーチンを含む)は、68000の促進命令がないにもかかわらずPascal呼び出し規約を使用しました。この呼び出し規約を使用すると、通常の呼び出しサイトで2〜4バイトのコードを節約できましたが、追加のパラメータを受け取るすべての関数の戻りサイトにある4〜6バイトのコード。
1つの例はMIPSで、オーバーフローのトラップと無視のためにそれぞれadd
とaddu
があります。 (また、sub
とsubu
です。)明示的にオーバーフローを処理するAdaなどの言語の最初のタイプの命令が必要でした(私は実際にAdaを使用したことはありません)。 Cのようなオーバーフローを無視する言語のタイプ。
私の記憶が正しければ、実際のCPUのALUには、オーバーフローを追跡するための追加の回路がいくつかあります。人々が気にかけた言語がCだけだったら、これは必要ありません。
これまで誰も言及しなかったように思われることの1つは、コンパイラーの最適化(基本言語がほとんど無関係)の進歩により、CISC命令セット(主に人間がコーディングするように設計された)からRISC命令セット(主にコンパイラーによってコーディングされるように設計されています。)
Burroughs 5000シリーズはALGOLを効率的にサポートするように設計され、IntelのiAPX-432はAdaを効率的に実行するように設計されました。 Inmos Transputerには独自の言語Occamがありました。パララックスの「プロペラ」プロセッサは、BASICの独自のバリアントを使用してプログラムされるように設計されたと思います。
これは言語ではありませんが、VAX-11命令セットには、VMS設計チームからの要求に基づいて設計されたプロセスコンテキストをロードする単一の命令があります。詳細は覚えていませんが、実装するのに非常に多くの指示を必要とするISTRは、スケジュールできるプロセスの数に深刻な上限を設けていました。
IBMのZシリーズメインフレームは、1960年代のIBM 360の子孫です。
特にCOBOLおよびFortranプログラムを高速化するためにそこに置かれたいくつかの命令がありました。古典的な例は、BXLE
–「インデックスが低いか等しい場合の分岐」であり、これはほとんどのFortran for
ループまたはCOBOL PERFORM VARYING x from 1 by 1 until x > n
単一の命令にカプセル化されています。
COBOLプログラムで一般的な固定小数点10進算術をサポートするパック10進命令のファミリ全体もあります。
Motorola 68000ファミリは、いくつかの autoincrement adressmode を導入し、CPUを介したデータのコピーを非常に効率的かつコンパクトにしました。
【更新例】
これは68000アセンブラに影響を与えたいくつかのc ++コードでした
while(someCondition)
destination[destinationOffset++] = source[sourceOffset++]
従来のアセンブラで実装されている(疑似コード、68000アセンブラコマンドを忘れた)
adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
move akku,(adressRegister1)
move (adressRegister2), akku
increment(adressRegister1, 1)
increment(adressRegister2, 1)
}
新しいadressmodeにより、これは
adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
move akku,(adressRegister1++)
move (adressRegister2++), akku
}
ループごとに4つではなく2つの命令のみ。
初期のIntel CPUには次の機能があり、その多くは64ビットモードで廃止されました。
多くのCPUのステータスレジスタにある符号フラグは、符号付きおよび符号なし演算を簡単に実行するために存在します。
SSE 4.1命令セットは、カウントおよびゼロ終了の両方のストリング処理(PCMPESTRなど)の命令を導入します
また、コンパイルされたコードの安全性をサポートするためにいくつかのシステムレベルの機能が設計されていることも想像できます(セグメント制限のチェック、パラメーターのコピーを伴う呼び出しゲートなど)。
一部のARMプロセッサー、主にモバイルデバイスのプロセッサー)には、ハードウェアJVMインタープリターであるJazelle拡張機能が含まれます。これは、Javaバイトコードを直接解釈します。Jazelle対応JVMはハードウェアを使用して実行を高速化し、JITの大部分を排除できますが、バイトコードをチップ上で解釈できない場合は、ソフトウェアへのフォールバックVMが保証されます。
そのようなユニットを持つプロセッサには、プロセッサを特別な「Jazelleモード」にするBXJ命令が含まれます。ユニットのアクティブ化に失敗した場合は、通常の分岐命令として解釈されます。ユニットは、JVM状態を保持するためにARMレジスタを再利用します。
Jazelleテクノロジーの後継は ThumbEE です。
私が知る限り、これは以前はより一般的でした。
質問のセッション で、James Goslingは、JVMバイトコードをより適切に処理できるハードウェアを作ろうとしている人がいたと言いましたが、これらの人は一般的な「一般的な"intel x86(たぶん、何らかの巧妙な方法でバイトコードをコンパイルしています)。
大企業が製品に巨額の資金を投じているため、一般的な人気のあるチップ(Intelなど)を使用することには利点があると彼は述べました。
ビデオはチェックアウトする価値があります。彼は19分か20分でこれについて話します。
Intel iAPX CPUは、OO言語用に特別に設計されています。ただし、うまくいきませんでした。
iAPX 432(intel Advanced Processor Architecture)は、Intelの最初の32ビットマイクロプロセッサデザインで、 1981年、3つの集積回路のセットとして。これは、1980年代のインテルの主要な設計となることを目的としており、多くの高度なマルチタスクおよびメモリ管理機能を実装しています。したがって、デザインはMicromainframe...と呼ばれていました。
IAPX 432は「完全に高級言語でプログラムされるように設計されました」。 Ada が主であり、サポートされています- オブジェクト指向プログラミング および ガベージコレクション ハードウェアで直接 マイクロコード 。さまざまな データ構造 の直接サポートは、iAPX 432の最新のオペレーティングシステムを、通常のプロセッサよりもはるかに少ないプログラムコードを使用して実装できるようにすることも目的としています。これらのプロパティと機能により、当時のほとんどのプロセッサ、特にマイクロプロセッサよりもはるかに複雑なハードウェアとマイクロコードの設計が実現しました。
当時の半導体技術を使用して、インテルのエンジニアは設計を非常に効率的な最初の実装に変換することができませんでした。これは、時期尚早のAdaコンパイラでの最適化の欠如に加えて、比較的遅いが高価なコンピュータシステムに貢献し、同じクロック周波数(1982年初頭)で新しい80286チップの約1/4の速度で典型的なベンチマークを実行しました。
低プロファイルで低価格の8086ラインに対するこの初期のパフォーマンスギャップは、Intel(後者はx86として知られています)をiAPX 432に置き換える計画が失敗した主な理由でした。エンジニアは次世代の設計を改善する方法を見てきましたが、iAPX 432機能アーキテクチャは、意図されていた簡素化サポートではなく、実装オーバーヘッドと見なされるようになりました。 。
IAPX 432プロジェクトはIntelにとって商業的な失敗でした...
私は簡単なページ検索を行いましたが、 Forth を実行するために特別に開発されたCPUについて誰も言及していないようです。 Forthプログラミング言語 はスタックベースでコンパクトで、制御システムで使用されます。
68000にはMOVEMがあり、これは複数のレジスターを単一の命令でスタックにプッシュするのに最も適しており、これは多くの言語が期待するものです。
コード全体でJSR(Jump SubRoutine)の前にMOVEM(MOVE Multiple)が見られた場合は、通常、C準拠のコードを扱っていることがわかりました。
MOVEMにより、宛先レジスタの自動インクリメントが可能になり、使用ごとに宛先でのスタックを継続したり、自動デクリメントの場合はスタックから削除したりできます。
8087数値コプロセッサが設計されたとき、言語はすべての浮動小数点演算を最高精度の型を使用して実行し、結果を低精度の変数に割り当てる場合は結果を低い精度に丸めるだけでした。たとえば、元のC標準では、シーケンスは次のとおりです。
float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;
a
とb
をdouble
にプロモートして追加し、c
をdouble
にプロモートして追加し、結果をfloat
に丸めて格納します。多くの場合、コンパイラがfloat
型で直接演算を実行するコードを生成する方が高速でしたが、変換するルーチンとともに、double
型でのみ動作する浮動小数点ルーチンのセットを使用する方が簡単でしたfloat
とfloat
の操作を処理するための別個のルーチンセットを持つことよりも、double
へ/から。 8087は、その算術アプローチを中心に設計されており、80ビット浮動小数点型を使用してすべての算術演算を実行します(おそらく次の理由により、80ビットが選択されました:
多くの16ビットおよび32ビットプロセッサでは、仮数と指数の間でバイトを分割する値を処理するよりも、64ビットの仮数と別個の指数を処理する方が高速です。
使用している数値タイプの完全な精度まで正確な計算を実行することは非常に困難です。たとえば、 log10(x)のようなものを計算する場合、64ビットタイプの1 ulp以内で正確な結果を計算し、前者を丸めるよりも、80ビットタイプの100 ulp以内で正確な結果を計算する方が簡単かつ高速です。結果を64ビット精度にすると、後者よりも正確な64ビット値が生成されます。
残念ながら、言語の将来のバージョンでは、浮動小数点型の動作方法のセマンティクスが変更されました。言語が一貫してそれらをサポートしていれば8087のセマンティクスは非常に優れていたでしょうが、関数f1()、f2()などがfloat
型を返す場合、多くのコンパイラ作成者はlong double
コンパイラの80ビット型ではなく64ビットdouble型のエイリアス(および80ビット変数を作成する他の手段を提供しない)、および次のようなものを任意に評価するため。
double f = f1()*f2() - f3()*f4();
次のいずれかの方法で:
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();
F3とf4がそれぞれf1とf2と同じ値を返す場合、元の式は明らかにゼロを返すはずですが、後者の式の多くはそうでない場合があります。これにより、最後の定式化は一般に3番目よりも優れており、拡張double型を適切に使用したコードを使用すれば、劣ることはほとんどないにしても、人々は8087の「特別な精度」を非難しました。
その後数年間で、インテルは、その動作を支持するように後のプロセッサを設計することにより中間結果をオペランドの精度に丸めることを強制するという言語の(IMHO不幸な)傾向に対応しました。中間計算の精度。
AtmelのAVRアーキテクチャは、完全にCでのプログラミングに適するように完全に設計されています。たとえば、この アプリケーションノート はさらに詳しく説明しています。
IMOこれは、rockets4kidsの優れた answer と密接に関連しており、初期のPIC16-sは直接アセンブラープログラミング(合計40命令)向けに開発されており、後のファミリはCをターゲットとしています。