半世紀の「ユニバーサル機械言語」(1966〜2016年):過去、現在、未来

KDPV

過去


物語は1962年にケンブリッジ大学でCPL (「ケンブリッジプログラミング言語」)-ALGOL-60の「改良版」で作業が開始されたときに開始できます。 大学院生のマーティン・リチャーズがこの言語の研究に参加しました。 新しいYPを実装する際の主な困難は、さまざまなコンピュータープラットフォーム用にコンパイラーを手動で移植する必要があるように思われました。 特に、Cambridge EDSAC-2が Atlas -2に置き換えられたとき、CPL開発者はコンパイラを新しいプラットフォームに移植するのに多くの時間を費やしました。

Martinの論文は「自己コンパイル」CPL専用でした。Martinによって開発されたコンパイラは、CPLの非常に単純化されたバージョンで記述され、そのコンパイラは当時のマクロアセンブラで簡単に記述できました。 CPLを新しいプラットフォームに移植するには、次の2つの手順を実行できます。
  1. 「簡易CPL」コンパイラを手動で記述します。
  2. 「フルCPL」のコンパイラをコンパイルします。

Martinはそこで止まらず、ポータブルコンパイラを開発するためのシステムであるBCPLを開発しました。 BCPLコンパイラは、Martin "OCODE"と呼ばれる擬似コードを生成しました。
OCODEは次のようになりました。
OCODE「デコード」(「プロコード」)
 94 5 L1 83 73 69 86 69
 95 4
 42 0
 42 0 40 2 14
 83
 42 0 42 1 40 2 14 83
 42 2
 40 3 42 1 15
 92
 85 L5
 90 L6
 42 1 40 4 40 2 14 83
 40 4 42 1 14 80 4 
 90 5 40 4 40 5 88 L6
 91 4
 42 2 40 3 42 1 15 92
 85 L7
 90 L8 40 4 40 2 14
 8 87 L9
 40 4 42 2 11 92
 85 L11
 90 L10
 42 0 40 6 40 2 14 83
 40 4 40 6 14 80 6
 90 L11
 40 6 40 3 22 86 L10
 91 6 90 L9
 40 4 42 1 14 80 4
 90 L7 40 4 40 5 88 L8
 91 4 97 103 0
エントリ5 L1 'S' 'I' 'E' 'V' 'E'
セーブ4
 LN 0
 LN 0 LP 2 PLUS
スティンド
 LN 0 LN 1 LP 2 PLUS STIND
 Ln 2
 LP 3 LN 1マイナス
店舗
ジャンプL5
 LAB L6
 LN 1 LP 4 LP 2 PLUS STIND
 LP 4 LN 1 PLUS SP 4
 LAB L5 LP 4 LP 5 ENDFOR L6
スタック4
 LN 2 LP 3 LN 1マイナスストア
ジャンプL7
 LAB L8 LP 4 LP 2 PLUS
 RV JF L9
 LP 4 LN 2マルチストア
ジャンプL11
 LAB L10
 LN 0 LP 6 LP 2 PLUS STIND
 LP 4 LP 6 PLUS SP 6
 LAB L11
 LP 6 LP 3 LS JT L10
スタック6ラボL9
 LP 4 LN 1 PLUS SP 4
 LAB L7 LP 4 LP 5 ENDFOR L8
スタック4 RTRNエンドプロック0
; 手順のタイトル
; スタックフレーム(2つのパラメーターと2つのローカル変数)
; スタックに番号0を置きます
; さらに0を入れ、2番目のスタック要素を追加します
; スタックの一番上の配列にその下の値を書き込む
; 配列の最初の要素についても同じ
; スタックに2番を置きます
; 3番目のスタック要素の値から1を引く
; 結果をローカル変数に書き込みます
; ラベルL5に移動
; ラベルタグL6
; スタックの4番目の要素を取得し、このインデックスで配列に書き込みます1
; スタック1の4番目の要素に追加し、結果を書き戻します
; L5:4番目のスタック要素<= 5番目の場合、ラベルL6に移動
; スタック上に4つの要素があることを発表
; 3番目のスタック要素の値から1を引く
; ラベルL7に移動
; L8:スタックの4番目と2番目の要素を追加する
; このアドレスの値を読み取ります。 0の場合、L9に進みます
; 4番目の要素に2を掛けます
; ラベルL11に移動
; タグラベルL10
; スタックの6番目の要素を取得し、このインデックス0で配列に書き込みます
; スタックの4番目の要素に4番目の要素を追加し、結果を書き戻します
; タグラベルL11
; スタックの7番目の要素が4番目よりも小さい場合は、ラベルL10に移動します
; スタックには6つの要素があります。 タグラベルL9
; スタック1の4番目の要素に追加し、結果を書き戻します
; L10:4番目のスタック要素<= 5番目の場合、L8に移動
; スタックには4つの要素があります。 手順の終わり
(スペースを節約するために、コマンドシーケンスは1行で記述されています。MartinはBCPLマニュアルでもまったく同じことを行っています。)

ソースパッケージBCPLをダウンロードします。
 LETふるい(workvec、vecsize)BE
 {
   workvec!0:= 0
   workvec!1:= 0
   FOR i = 2 to vecsize-1 DO workvec!I:= 1
   FOR i = 2 TO vecsize-1 DO
     IF workvec!します
     {LET j = 2 * i
       j <vecsize DOの場合
       {workvec!j:= 0
         j:= j + i
       }
     }
 }
OCODEの新しいバージョンでは、浮動小数点数のサポートが追加され(それぞれ、サポートされるオペコードのセットがほぼ2倍になりました)、 ENDFORオペコードがENDFORENDFOR -代わりに、 LE JTペアLE JT生成されます。

「ユニバーサルマシン言語」の中で、OCODEは、そのラベルが特別な指示によって決定されるという点でユニークです。 プログラムを解釈するには、まずすべてをメモリにロードし、その中のラベルを見つける必要があります。
-そして、別のプログラムであるコードジェネレーターが 、このような擬似コードを含むファイルを最終プロセッサ用の実行可能プログラムに変換しました。 OCODEは、スペースと改行で区切られた10進数のテキストファイルとして保存されました。OCODEの開発中、ファイル形式を特定のバイトサイズにリンクすると、そのようなファイルの移植性が制限されました。

BCPL (1)コンパイラはOCODEとして出荷されており、新しいプラットフォームに移植するために必要でした:
  1. 擬似コードインタープリターを手動で記述します(2) (任意の言語、BASICでも)。
  2. (3) BCPLで記述されたコードジェネレーターをプラットフォームに適合させます。
  3. インタープリター(2)の下でBCPLコンパイラー(1)を実行し、コードジェネレーター(3)にフィードし、コードジェネレーターの実行可能ファイル(4)を取得します。
    • 通訳者(2)は、今後必要ありません。
  4. コンパイラー擬似コード(1)をコードジェネレーター(4)から追い出し、出力でコンパイラー実行可能ファイルを取得します。


このアプローチは、コンパイラを新しいプラットフォームに移植するために最低限の低レベルのプログラミングのみが必要であることを意味していました。 実際、BCPLの実装は1967年までに完了しました。数年前に始まったCPLの実装が完了する前です!

システムプログラミングに対するBCPLのメリットは、ケントンプソンにインスピレーションを与えてBe言語を作成し、ケンの同僚であるデニスリッチーにインスピレーションを与えてCを作成しました。 BCPLから伝統が{中括弧}プログラムブロックを指定するようになり、BCPL 最初のプログラム「Hello、World!」 作成されました
 「libhdr」を取得

 LET開始()= VALOF
 {writef( "Hello * n")
  結果0
 }
BCPLが歴史上ダウンした理由は私たちにとってより重要です:OCODEは最初の普遍的な「命令セットアーキテクチャ」 (ISA)です。 「仮想マシン」。その機能により特定のハードウェアプラットフォームに関連付けられていません。 したがって、BCPLは、 「一度書き込み、どこでも実行」WORAパラダイムに準拠する最初のプログラミング言語です。BCPLプログラムは、コンパイルされた形式で配布でき、OCODEコードジェネレーターが存在するプラットフォームで実行できます。

BCPL自体とそのOCODEは、英国以外では人気を博していませんが、 WORAの考え方は定着しています。 1974年、すでに大陸で、 ETHチューリッヒで 、ニクラウスヴィルトの指導の下で、「Pascal-P」が開発されました-中間「pコード」へのコンパイルと、この「pコード」の特定のハードウェアプラットフォーム用の実行可能コードへのコンパイル。 海洋の反対側であるUCSDの Pascal-Pに基づいて、 p-System (1978)OSが作成されました。コードジェネレーターはまったくありませんでした 。ベアメタルでの作業。 すべてのp-Systemシステムユーティリティは、クロスプラットフォームのp-codeの形式で提供され、p-Systemを新しいハードウェアプラットフォームに適合させるには、p-codeインタープリターを記述するだけで十分でした。

Pコードの例
 0000:D8 p2_0:SLDL 1
 0001:00 SLDC 0
 0002:9A STO
 0003:02 SLDC 2
 0004:CC 03 STL 3
 0006:DA p2_2:SLDL 3
 0007:31 SLDC 49
 0008:C8 LEQI
 0009:A1 12 FJP p2_5
 000B:D8 SLDL 1
 000C:DA SLDL 3
 000D:01 SLDC 1
 000E:32 SLDC 50
 000F:88 CHK
 0010:01 SLDC 1
 0011:95 SBI
 0012:A4 01 IXA 1
 0014:01 SLDC 1
 0015:9A STO
 0016:DA SLDL 3
 0017:01 SLDC 1
 0018:82 ADI
 0019:CC 03 STL 3
 001B:B9 F6 UJP p2_2
 001D:02 p2_5:SLDC 2
 001E:CC 03 STL 3
 0020:DA p2_4:SLDL 3
 0021:31 SLDC 49
 0022:C8 LEQI
 0023:A1 2F FJP p2_1
 0025:D8 SLDL 1
 0026:DA SLDL 3
 0027:01 SLDC 1
 0028:32 SLDC 50
 0029:88 CHK
 002A:01 SLDC 1
 002B:95 SBI
 002C:A4 01 IXA 1
 002E:F8 SIND 0
 002F:A1 1C FJP P2_6
 0031:02 SLDC 2
 0032:DA SLDL 3
 0033:8F MPI
 0034:CC 02 STL 2
 0036:D9 p2_3:SLDL 2
 0037:32 SLDC 50
 0038:C9 LESI
 0039:A1 12 FJP p2_6
 003B:D8 SLDL 1
 003C:D9 SLDL 2
 003D:0​​1 SLDC 1
 003E:32 SLDC 50
 003F:88 CHK
 0040:01 SLDC 1
 0041:95 SBI
 0042:A4 01 IXA 1
 0044:00 SLDC 0
 0045:9A STO
 0046:D9 SLDL 2
 0047:DA SLDL 3
 0048:82 ADI
 0049:CC 02 STL 2
 004B:B9 F4 UJP P2_3
 004D:DA P2_6:SLDL 3
 004E:01 SLDC 1
 004F:82 ADI
 0050:CC 03 STL 3
 0052:B9 F2 UJP p2_4
 0054:AD 00 P2_1:RNP 0
; 最初のパラメーターをスタックにプッシュします
; スタックに0を置きます
; スタックの一番上の値をその下のアドレスに書き込みます
; スタックに2番を置きます
; スタックの最上部の値をスタックフレームに(ローカル変数に)書き込む
; ローカル変数をスタックにプッシュします

; 49以下ですか?
; そうでない場合は、ラベルp2_5に移動します




; 配列の境界を確認してください:ローカル変数#3は1から50の間でなければなりません

; スタックの一番上の値から1を引く、つまり ローカル変数3から
; 配列要素へのポインタを計算します:index-前回の減算結果、
; baseは最初のパラメーター、要素サイズは1ワード
; 計算ポインタによるユニットの書き込み


; ローカル変数3の値に1を追加します
; 結果をスタックフレームに書き戻す
; ラベルp2_2への無条件ジャンプ

; ローカル変数番号3にデュースを書き込む


; この変数は49以下ですか?
; そうでない場合は、ラベルp2_1に移動します




; 配列の境界を確認してください:ローカル変数#3は1から50の間でなければなりません


; 上記とまったく同じように、配列要素へのポインタを計算します
; 計算されたポインタからオフセット0のワードを読み取ります
; ゼロ値が読み取られた場合、ラベルp2_5に移動します


; ローカル変数3の値に2を掛けます
; スタックフレーム内に結果を書き込む


; この値は50未満ですか?
; そうでない場合は、ラベルp2_6に移動します




; 配列の境界を確認してください:ローカル変数#2は1から50の間でなければなりません


; 配列要素へのポインタを計算します:インデックスは1つ少なくなります
; ローカル変数2の値、ベースおよびサイズ-上記のように
; 計算されたポインターにゼロを書き込む


; ローカル変数2に変数3の値を追加します
; 結果をスタックフレームに書き戻す
; ラベルp2_3への無条件ジャンプ


; ローカル変数3の値に1を追加します
; 結果をスタックフレームに書き戻す
; ラベルp2_4への無条件ジャンプ
; スタックから呼び出し側プロシージャに0の値を返します

ソースパッケージpascalをダウンロードします。
 const data_size = 50;
タイプdata_array =ブール値の配列[1..data_size];

手続きふるい(var workvec:data_array);
 var i、j:整数;
始める
   workvec [1]:= false;
   for i:= 2 to data_size-1 do workvec [i]:= true;
   for i:= 2 to data_size-1 do
     workvec [i]の場合、開始
       j:= 2 * i;
      一方、j <data_sizeは開始します
         workvec [j]:= false;
         j:= j + i;
      終わり
    終わり
終わり;
機械語整数と浮動小数点数のみをサポートするOCODEと比較して、Pascalのpコードは、文字列、配列、「パック配列」(要素サイズが1ワード未満)など、はるかに多様なデータ型をサポートします。記録、セットなど。 動的メモリ割り当ても同様です。 もう1つの重要な違いは、プロシージャの「作業スタック」と、パラメータとローカル変数を含む「スタックフレーム」が分離されたことです。

コンパクトなpコードの場合、多くの命令には、小さく頻繁に使用される引数値の「短縮オペコード」があります。

p-Systemは非常に人気がありました。いくつかのApple IIモデルと、元のIBM PC(1981)を含むいくつかのIBMコンピューターがOSとしてp-Systemとともに提供されました。 1979年から、Western Digitalはp-code実行がハードウェアに実装されたPascal MicroEngineミニコンピューターをリリースしました。 したがって、抽象的な「ユニバーサルマシン言語」からのpコードは、実際のコンピューターの実際のマシン言語に変わりました。

p-Systemは90年代にはすでに市場から姿を消していたが、1996年にJames GoslingにJava仮想マシンの作成を促すことに成功した。 「一度書くだけで、どこでも実行できます!」人気の広告スローガンに変わり、「ユニバーサルマシン言語」( 「バイトコード」 )の実装が2つの方向に並行して開発されました。

当然のことながら、「バイトコードプロセッサ」のどれも-PコードでもJavaでも-商業的に成功しませんでした。 (これには、以前のIntel iAPX 432 (1981)プロセッサ、Adaのバイトコードのハードウェア実装も含まれます。)Javaバイトコード、Wirthのpコード、およびOCODEは、すべて中間指向であるため、すべてスタック指向です。生成と解釈が最も簡単です。 「仮想マシン」スタックは制限されていません。 反対に、「鉄」プロセッサには固定数のレジスタがあります。コンパイラの最も重要なタスクの1つは、メモリアクセスができる限り実行されないようにレジスタ間でデータを分散することです。 「鉄」のデータ間の依存関係を追跡し、「鉄」レジスタに従ってデータを配布し、「鉄」の制限に適合するようにデータへのアクセスを再配置するには、非常に複雑なメカニズムが必要です。 同じレベルの半導体技術では、同じハードウェアで同じバイトコードを実行するよりも、単純なISA用のプロセッサを作成し、その上にバイトコード変換を実装する方が効率的です。 何度も何度も、夢のようなIT起業家は、「ユニバーサルマシン言語」を実際の言語に変えることは、技術的には可能ですが、商業的には見込みがないと確信するようになりました。

Javaバイトコードの例


 2a
なる
 3c
 2a
 03
 03
 54
 2a
 04
 03
 54
 05
 3d
 1c
 1b
 a2 00 0d
 2a
 1c
 04
 54
 84 02 01
 a7 ff f4
 05
 3d
 1c
 1b
 a2 00 23
 2a
 1c
 33
 99 00 17
 05
 1c
 68
 3e
 1d
 1b
 a2 00 0e
 2a
 1d
 03
 54
 1d
 1c
 60
 3e
 a7 ff f3
 84 02 01
 a7 ff de
 b1
プライベート静的ボイドシーブ(ブール[]);
 コード:
     0:aload_0 //インデックス0のスタックフレームから取得したリンクをスタックに配置します。 パラメータ
     1:arraylength //渡された配列の長さをスタックにプッシュします
     2:istore_1 //スタックから整数をインデックス1のスタックフレームに保存します
     3:aload_0       
     4:iconst_0 //スタックに整数を置く-定数0
     5:アイコン      
     6:bastore //インデックス0でバイトまたはブール配列0に書き込む
     7:aload_0       
     8:iconst_1      
     9:アイコン
    10:bastore //同じ配列0にインデックス1で書き込む
    11:iconst_2      
    12:istore_2 //整数値2をインデックス2のスタックフレームに保存します
    13:iload_2 //保存した値をスタックに配置します
    14:iload_1 //ローカル変数番号1をスタックに配置します
    15:if_icmpge 28 // 2つの整数を比較します。  value> =変数の場合、命令28に進みます
    18:aload_0       
    19:iload_2       
    20:iconst_1      
    21:bastore //ローカル変数のインデックスにあるユニットをバイトまたはブール配列に書き込む
    22:iinc 2、1 //ローカル変数2を1つ増やす
    25:goto 13 //命令13に移動
    28:iconst_2      
    29:istore_2 //ローカル変数2に整数2を書き込む
    30:iload_2       
    31:iload_1
    32:if_icmpge 67 //変数番号2が変数番号1以上の場合、命令67に進む
    35:aload_0       
    36:iload_2       
    37:baload //この変数からインデックスによってバイトまたはブール配列の要素を読み取ります
    38:ifeq 61 //読み取り項目がゼロの場合、命令61に進みます
    41:iconst_2      
    42:iload_2       
    43:imul //ローカル変数No. 2の値に2を掛ける
    44:istore_3 //結果をインデックス3のスタックフレームに保存する
    45:iload_3       
    46:iload_1       
    47:if_icmpge 61 //ローカル変数#3> =変数#1の場合、命令61に進む
    50:aload_0       
    51:iload_3       
    52:アイコン      
    53:bastore //変数No. 3からのインデックスによってバイトまたはブール配列0に書き込みます
    54:iload_3       
    55:iload_2       
    56:iadd //ローカル変数No. 3変数No. 2の値に追加
    57:istore_3 //結果をスタックフレームに書き戻す
    58:goto 45 //命令45に移動
    61:iinc 2、1 //ローカル変数2を1つ増やす
    64:goto 30 //命令30に移動
    67:return //プロシージャから戻る

ソースコード:
  private static void sieve(boolean[] workvec) { int vecsize = workvec.length; workvec[0] = false; workvec[1] = false; for(int i = 2; i<vecsize; i++) workvec[i] = true; for(int i = 2; i<vecsize; i++) if(workvec[i]) { int j = 2 * i; while(j < vecsize) { workvec[j] = false; j = j + i; } } } 

バイトコードはWirthのpコードよりもさらに設計されており、コンパクトに設計されています。たとえば、 iflt (ゼロと条件SLDC 0; GEQI; FJP比較)は3つの命令SLDC 0; GEQI; FJP対応しSLDC 0; GEQI; FJP SLDC 0; GEQI; FJP SLDC 0; GEQI; FJP 、およびiincは4つのSLDL; SLDC; ADI; STL命令を置き換えSLDL; SLDC; ADI; STL SLDL; SLDC; ADI; STL SLDL; SLDC; ADI; STL

ただし、反対の例があります。 新しいISAとそのコンパイラを作成し、新しいプラットフォームに必要な最小限のソフトウェアが現れるのを待つよりも、既存のプログラムが新しいプロセッサで実行できるように、人気のISAを実装することはプロセッサメーカーにとってはるかに有益です。 IA-32は長い間、このような「ユニバーサルマシン言語」のままでした。既存のソフトウェアの量は、新しいISAへの切り替えの利点を上回り、 Pentium Pro (1995)以降、Intelプロセッサは本質的に古いISAの「ハードウェアエミュレーション」を実装しました。 8つの「クラシック」レジスタで動作するIA-32命令は、プロセッサにより40の「鉄」レジスタで動作する内部RISCマイクロコマンドに変換されます。 プロセッサ内のパイプラインは、これらのRISCマイクロコマンドをいくつかのスレッドに実行します-これがデータ間の依存関係を壊さない場合、マイクロコマンドを任意に再配置します。 開発チームにLinus Torvaldsを含むTransmeta Crusoeプロセッサ (2000)では、ISAハードウェアエミュレーションのアイデアがさらに開発されました。箱から出してIA-32をサポートしましたが、他のISAと連携するように再プログラムできます-これを実証するためにTransmetaには、ハードウェアJavaバイトコードをサポートするCrusoeの「広告サンプル」がありました。

IA-32マシンコードの例
機械コードアセンブラー



 55
 89 e5
 56
 66 c7 01 00 00
 b8 02 00 00 00
 02 00 00 00
 eb 05

 c6 04 31 01
 46

 39 d6
 7c f7
 eb 01

 40

 39 d0
 7d 17

 80 3c 01 00
 74 f5

 8d 34 00
 eb 06

 c6 04 31 00
 01 c6

 39 d6
 7c f6
 eb e4

 5e
 5d
 c3
	 .type _ZL5sievePbi、@関数
 _ZL5sievePbi:#ecxに渡される配列へのポインター、edxへの配列の長さ
 #%エントリ
	 pushl%ebp#以前のebp値を保持
	 movl%esp、%ebp#ebpは現在のスタックフレームを指すようになりました
	 pushl%esi#以前のesi値を保持
	 movw $ 0、(%ecx)#ecxにゼロワードを書き込む
	 movl $ 2、%eax#(つまり、配列の2つの要素が1つの命令でリセットされます)
	 movl $ 2、%esi#eaxとesiで2つ節約
	 jmp .LBB1_1#ラベル.LBB1_1に移動
 .LBB1_2:#%for.body
	 movb $ 1、(%ecx、%esi)#ecx + esiにシングルバイトを書き込む
	 incl%esi#esiを1つ増やします
 .LBB1_1:#.for.cond
	 cmpl%edx、%esi#esiとedxを比較
	 jl .LBB1_2#edx <esiの場合、ラベル.LBB1_2に移動
	 jmp .LBB1_4#それ以外の場合、ラベル.LBB1_4に移動
 .LBB1_3:#%for.inc.11
	 incl%eax#eaxを1つ増やします
 .LBB1_4:#.for.cond.4
	 cmpl%edx、%eax#eaxとedxを比較
	 jge .LBB1_9#edx> = esiの場合、ラベル.LBB1_9に移動
 #%for.body.7
	 cmpb $ 0、(%ecx、%eax)#ecx + esiでゼロバイトと比較
	 je .LBB1_3#ゼロの場合、.LBB1_3に移動
 #%if.then
	 leal(%eax、%eax)、%esi#eaxの二重値をesiに書き込む
	 jmp .LBB1_7#ラベル.LBB1_7に移動
 .LBB1_8:#%while.body
	 movb $ 0、(%ecx、%esi)#ゼロバイトをecx + esiに書き込む
	 addl%eax、%esi#eaxをesiに追加
 .LBB1_7:#%while.cond
	 cmpl%edx、%esi#esiとedxを比較
	 jl .LBB1_8#edx <esiの場合、ラベル.LBB1_8に移動
	 jmp .LBB1_3#それ以外の場合、ラベル.LBB1_3に移動
 .LBB1_9:#%for.cond.cleanup.6
	 popl%esi#古いesi値を復元
	popl %ebp #    ebp
	retl #   
, , .

「ユニバーサルマシン言語」のハードウェア実装のこれまでのところ成功した例で、実装されたISAがスタックされずに登録されたことは偶然ではありません。 OCODEを置き換えるために、Martin Richards自身がINTCODE(1972)と呼ばれる新しいレジストリuni-ISAを開発しました。 INTCODEは非常にシンプルです。6つのレジスタ、8つの操作、およびいくつかのアドレッシングモードがサポートされています。命令は、アドレス指定モードに応じて、1ワードまたは2ワードを占有します。 1980年に、Martinは16ビットマイコン用のこのuni-ISAのバージョンを開発しました。新しいuni-ISA-まだ6つのレジスタがありますが、より複雑で直交性の低いコマンドセットがあります-はCintcodeと呼ばれ、非常にコンパクトでした。広告リーフレットで承認されましたCintcodeプログラムは、通常、6502マシンコードにコンパイルされるよりも3倍少ないメモリを使用しますコンピューターの場合、BBC MicroおよびAmigaは、マシンコードとともにCintcodeの実行をサポートする一般的なOSでした。

Cintcodeの例
P — ; P[3] , P[4] — .

 0: 10 L0 ; A := 0
 1: DB ST0P3 ; P[3][0] := A (   )
 2: DD ST1P3 ; P[3][1] := A (   )
 3: 0F LM1 ; A := -1
 4: C4 AP4 ; A := A + P[4] (    )
 5: A6 SP6 ; P[6] := A (   )
 6: 12 L2 ; B := A; A := 2
 7: A5 SP5 ; P[5] := A
 8: 5C0A JLS 10 ; IF B<A GOTO 19
10: 11 L1 ; A := 1
11: 83 LP3 ; B := A; A := P[3]
12: 9A STP5 ; P[5][A] := B (     P[5])
13: B5 XCH ; A := B
14: C5 AP5 ; A := A + P[5]
15: A5 SP5 ; P[5] := A (  P[5]  )
16: 86 LP6 ; B := A; A := P[6]
17: 9CF8 JLE -8 ; IF B<=A GOTO 10
19: 0F LM1 ; A := -1
20: C4 AP4 ; A := A + P[4]
21: A6 SP6 ; P[6] := A (     )
22: 12 L2 ; B := A; A := 2
23: A5 SP5 ; P[5] := A
24: 5C1B JLS 27 ; IF B<A GOTO 52
26: 83 LP3 ; A := P[3]
27: D8 RVP5 ; A := P[5][A] (     P[5])
28: 1E11 JEQ0 17 ; IF A=0 GOTO 46
30: 85 LP5 ; A := P[5]
31: 12 L2 ; B := A; A := 2
32: 34 MUL ; A := A * B (  P[5])
33: A7 SP7 ; P[7] := A
34: 84 LP4 ; B := A; A := P[4] ( A  )
35: BC0A JGE 10 ; IF B>=A GOTO 46
37: 10 L0 ; A := 0
38: 87 LP7 ; B := A; A := P[7]
39: 98 STP3 ; P[3][A] := B (     P[7])
40: 87 LP7 ; A := P[7]
41: C5 AP5 ; A := A + P[5]
42: A7 SP7 ; P[7] := A (  P[7]  P[5])
43: 84 LP4 ; B := A; A := P[4] ( A  )
44: 5CF8 JLS -8 ; IF B<A GOTO 37
46: 11 L1 ; A := 1
47: C5 AP5 ; A := A + P[5]
48: A5 SP5 ; P[5] := A (  P[5]  )
49: 86 LP6 ; B := A; A := P[6] (A  1   )
50: 9CE7 JLE -25 ; IF B<=A GOTO 26
52: 7B RTN

(2014) BCPL Cintcode System BCPL. Cintcode ( A B ) ( A ).


積み重ねられたuni-ISAの開発の頂点は、または、反対側から見ると、開発の行き止まりです。MSIL(2001;正式にCILと呼ばれています)です。MSILはJavaバイトコードに非常に似ていますが、いくつかの追加機能追加します私の知る限り、ハードウェアにMSILを実装する試みは行われていません。マイクロソフトは、クロスプラットフォームでフル機能のWebアプリケーションを作成するためのJavaの代替案を提案しようともしていません。MSILは「Microsoftプラットフォームのマシン言語」のままであり、他にはありません。

MSILの例
.method private hidebysig static
        void sieve(bool[] workvec) cil managed
 {
  .maxstack 3
  .locals init ([0] int32 vecsize,
                [1] int32 i,
                [2] int32 V_2,
                [3] int32 j)
  IL_0000: /* 02 | */ ldarg.0 //     №0 ()
  IL_0001: /* 8E | */ ldlen //    ( )
  IL_0002: /* 69 | */ conv.i4 //    int32 (  )
  IL_0003: /* 0A | */ stloc.0 //      №0
  IL_0004: /* 02 | */ ldarg.0 //    
  IL_0005: /* 16 | */ ldc.i4.0 //     0 (  )
  IL_0006: /* 16 | */ ldc.i4.0
  IL_0007: /* 9C | */ stelem.i1 //        
  IL_0008: /* 02 | */ ldarg.0
  IL_0009: /* 17 | */ ldc.i4.1 //     1 (  )
  IL_000a: /* 16 | */ ldc.i4.0
  IL_000b: /* 9C | */ stelem.i1 //     (int8)    1
  IL_000c: /* 18 | */ ldc.i4.2
  IL_000d: /* 0B | */ stloc.1 //      №1
  IL_000e: /* 2B | 08 */ br.s IL_0018 //      IL_0019
  IL_0010: /* 02 | */ ldarg.0
  IL_0011: /* 07 | */ ldloc.1 //      
  IL_0012: /* 17 | */ ldc.i4.1
  IL_0013: /* 9C | */ stelem.i1 //        
  IL_0014: /* 07 | */ ldloc.1
  IL_0015: /* 17 | */ ldc.i4.1
  IL_0016: /* 58 | */ add //      
  IL_0017: /* 0B | */ stloc.1 //     
  IL_0018: /* 07 | */ ldloc.1
  IL_0019: /* 06 | */ ldloc.0 //      №0
  IL_001a: /* 32 | F4 */ blt.s IL_0010 //   №1 <  №0,   IL_0010
  IL_001c: /* 18 | */ ldc.i4.2
  IL_001d: /* 0C | */ stloc.2 //      №2
  IL_001e: /* 2B | 1B */ br.s IL_003b //      IL_003b
  IL_0020: /* 02 | */ ldarg.0
  IL_0021: /* 08 | */ ldloc.2
  IL_0022: /* 90 | */ ldelem.i1 //       
  IL_0023: /* 2C | 12 */ brfalse.s IL_0037 //    ,   IL_0037
  IL_0025: /* 18 | */ ldc.i4.2
  IL_0026: /* 08 | */ ldloc.2
  IL_0027: /* 5A | */ mul //     №2  
  IL_0028: /* 0D | */ stloc.3 //      №3
  IL_0029: /* 2B | 08 */ br.s IL_0033 //      IL_0033
  IL_002b: /* 02 | */ ldarg.0
  IL_002c: /* 09 | */ ldloc.3
  IL_002d: /* 16 | */ ldc.i4.0
  IL_002e: /* 9C | */ stelem.i1 //  0       №3
  IL_002f: /* 09 | */ ldloc.3
  IL_0030: /* 08 | */ ldloc.2
  IL_0031: /* 58 | */ add //     №2  №2
  IL_0032: /* 0D | */ stloc.3 //     №2
  IL_0033: /* 09 | */ ldloc.3
  IL_0034: /* 06 | */ ldloc.0
  IL_0035: /* 32 | F4 */ blt.s IL_002b //   №3 <  №0,   IL_002b
  IL_0037: /* 08 | */ ldloc.2
  IL_0038: /* 17 | */ ldc.i4.1
  IL_0039: /* 58 | */ add //      №2
  IL_003a: /* 0C | */ stloc.2 //     
  IL_003b: /* 08 | */ ldloc.2
  IL_003c: /* 08 | */ ldloc.0
  IL_003d: /* 32 | E1 */ blt.s IL_0020 //   №2 <  №0,   IL_0020
  IL_003f: /* 2A | */ ret //   
 }

C# Java booleanbool . Java- MSIL : -, , . -, (/ , , ..), MSIL, add , «», .. ; (, — int32) . , MSIL JIT-, MSIL- .

, MSIL «», iflt iinc .

この千年紀には、「ユニバーサルマシン言語」に対するスタックの競合はなくなりました。

プレゼント


2000年、UIUCの大学院生であるChris Luttner は卒業プロジェクトとして、Javaのコードを普遍的な「中間表現」(IR)変換する完全に独立した「フロントエンド」で構成される「ユニバーサルコンパイラ」LLVMの開発を開始しました。バックエンド。IRを特定のISAの実行可能コードに変換します。中間表現LLVM-IRとして、単一の割り当てフォームが選択されました「SSAフォーム」
):各変数には値が1回割り当てられ、プログラムの動作中は変更されません。このフォームは、データ間の依存関係の追跡を簡素化し、個々の命令の置換と置換、重複または「デッド」命令の検出と削除などを単純化します

LLVM-IRの例
(BB), ( ). BB . — . BB , .. BB, .

 ; Function Attrs: minsize noinline nounwind optsize
define internal fastcc void @_ZL5sievePbi(i8* nocapture %workvec, i32 %vecsize) #0 {
 ; #0 --     (  )
entry:
  store i8 0, i8* %workvec, align 1, !tbaa !1 ;      
  %arrayidx1 = getelementptr inbounds i8, i8* %workvec, i32 1 ;      
  store i8 0, i8* %arrayidx1, align 1, !tbaa !1 ;      
  br label %for.cond ;     BB

for.cond: ; preds = %for.body, %entry
  %i.0 = phi i32 [ 2, %entry ], [ %inc, %for.body ]
 ; %i.0   2,    BB   %entry,   %inc,    %for.body
  %cmp = icmp slt i32 %i.0, %vecsize
 ;  %i.0  %vecsize   ,    %cmp  1  0
  br i1 %cmp, label %for.body, label %for.cond.4 ;    %for.body  %for.cond.4

for.body: ; preds = %for.cond
  %arrayidx2 = getelementptr inbounds i8, i8* %workvec, i32 %i.0 ;    %i.0-  
  store i8 1, i8* %arrayidx2, align 1, !tbaa !1 ;      
  %inc = add nuw nsw i32 %i.0, 1 ;  %inc  %i.0  
 ; nuw  nsw ,     (  ),    
  br label %for.cond ;     BB

for.cond.4: ; preds = %for.cond, %for.inc.11
  %i3.0 = phi i32 [ %inc12, %for.inc.11 ], [ 2, %for.cond ]
 ; %i3.0   2,    BB   %for.body,   %inc12,   %for.inc.11
  %cmp5 = icmp slt i32 %i3.0, %vecsize ;  %i3.0  %vecsize   
  br i1 %cmp5, label %for.body.7, label %for.cond.cleanup.6 ;  ,  %i3.0  %vecsize

for.cond.cleanup.6: ; preds = %for.cond.4
  ret void ;   ,   

for.body.7: ; preds = %for.cond.4
  %arrayidx8 = getelementptr inbounds i8, i8* %workvec, i32 %i3.0
  %0 = load i8, i8* %arrayidx8, align 1, !tbaa !1, !range !5 ;       %i3.0
  %tobool = icmp eq i8 %0, 0 ;      
  br i1 %tobool, label %for.inc.11, label %if.then

if.then: ; preds = %for.body.7
  %mul = shl nsw i32 %i3.0, 1 ;  %mul   %i3.0
  br label %while.cond ;     BB

while.cond: ; preds = %while.body, %if.then
  %j.0 = phi i32 [ %mul, %if.then ], [ %add, %while.body ]
  %cmp9 = icmp slt i32 %j.0, %vecsize ;  %j.0  %vecsize   
  br i1 %cmp9, label %while.body, label %for.inc.11 ;  %j.0 < %vecsize,   %while.body

while.body: ; preds = %while.cond
  %arrayidx10 = getelementptr inbounds i8, i8* %workvec, i32 %j.0
  store i8 0, i8* %arrayidx10, align 1, !tbaa !1 ;    %j.0-  
  %add = add nsw i32 %j.0, %i3.0 ;  %add  %j.0  %i3.0
  br label %while.cond ;     BB

for.inc.11: ; preds = %while.cond, %for.body.7
  %inc12 = add nuw nsw i32 %i3.0, 1 ;  %inc12  %i3.0  
  br label %for.cond.4
 }

IA-32 LLVM IR — , , , BB .

!tbaa !1alias analysis ; !range !5 — ( bool — 0 1).

, — «» MSIL.

C++:
 static void sieve(bool workvec[], int vecsize) { workvec[0] = false; workvec[1] = false; for(int i = 2; i<vecsize; i++) workvec[i] = true; for(int i = 2; i<vecsize; i++) if(workvec[i]) { int j = 2 * i; while(j < vecsize) { workvec[j] = false; j = j + i; } } } 

LLVM-IRは、ユニISAとしてではなく、主にフロントエンドからバックエンドにコードを転送するための効果的な方法として開発されたため、ユニISAの役割には多くの欠点があります。まず、ターゲットプラットフォームの機能がLLVM-IRの生成に影響する可能性があります。32ビットシステム用のプログラムは、64ビットシステム用のプログラムとは別のLLVM-IRにコンパイルされます。また、Windows用のプログラムは、Linux用のプログラムとは異なるLLVM-IRでコンパイルされます。次に、LLVM-IRは不安定です。LLVM開発者は、重要なプラットフォームで重要な言語ツールをより効率的にコンパイルできる場合、新しい命令を追加したり、既存の命令の値を変更したりできます。これは、特定のバージョンのフロントエンドによって生成されたLLVM-IRは、対応するバージョンのバックエンドでのみ使用でき、他のバージョンでは使用できないことを意味します。第三に、LLVM-IRでは、コードに「未定義の動作」を提示できます-各バックエンドには、プログラムから最大限のパフォーマンスを「絞り出す」ために、便利な方法でそのようなコードをブロードキャストする権利があります。 CまたはC ++で未定義の動作を伴うコードは、通常、LLVM-IRで未定義の動作を伴うコードにコンパイルされます。実際、このようなコードのセマンティクスは、特定のプラットフォームに最適な方法で、フロントエンドではなくバックエンドによって決定されます。当然のことながら、未定義の動作を伴うコードは、uni-ISAが昇格される「1回書き込み、どこでも実行」というパラダイムと矛盾します。それにもかかわらず、LLVMのアクティブな開発者の1人であるGoogleは、LLVMがサポートする多くのPLのいずれかで書かれたクロスプラットフォームアプリケーションの配布にLLVM-IRが適切な形式であると考えました。バージョン31(2013)以降、Google ChromeにはPNaClのサポートが含まれています-LLVM-IRのサブセットで、安定した命令セマンティクスを備えており、プラットフォーム固有の構成と最適化はありません。

15年前にまったく同じ目的でクロスプラットフォームのフル機能Webアプリケーションを作成するために発明されたJavaアプレットよりもPNaClアプリケーションの方が優れているのはなぜでしょうか。 (このトピックの冒頭のKDPVはまさにトピックです。)私はPNaClの2つの利点を知っています。まず、Javaスタックバイトコードと比較したLLVM-IRは、ブラウザーがPNaClアプリケーションを特定のターゲットプラットフォームのマシンコードに変換するときのコード最適化を簡素化します。 2番目の、そして私が理解するように、主な議論はLLVMのオープン性とライセンスされた清潔さです。そのホスト(SunとOracle)はJavaサポートに関してGoogleを含む彼らが出会ったすべての人を訴えました。対照的に、LLVMとそのIRは完全にオープンです。そして一方では、GoogleはPNaClコンパイラであらゆる種類のパンと他のLLVM開発参加者によって実装された新しい最適化で無料で受け取ります。一方、他の開発者は、ブラウザにPNaClアプリケーションのサポートを追加する権利があり、訴訟を恐れることはありません。

これまでのところ、Googleの例に従ってPNaClのサポートを実装する意思はありませんでした。それどころか、同じ目的のためのMozilla-クロスプラットフォームでフル機能のWebアプリケーションを作成する-は、asm.js(2013)と呼ばれる独自のuni-ISAを開発しましたAsm.js生成は、LLVMの新しいバックエンドとして実装されました。 asm.jsとPNaClの作業はほぼ同時に行われ、Google Chromeではasm.jsのサポートはPNaClのサポートよりも早く登場しました-バージョン28(2013)以降。

asm.jsは非常に洗練されたアイデアを実装しています。そのプログラムはJavaScriptの(大幅に削除された)正しいコードです。したがって、このようなプログラムはJavaScriptをサポートするブラウザーで動作するはずです。一方、asm.jsコンストラクトを認識し、ターゲットプラットフォームのマシンコードにオンザフライでコンパイルできるブラウザは、JavaScriptを「額で」実行するブラウザよりも何倍も高速にプログラムを実行します。したがって、両方の古いブラウザはasm.jsで新しいコードを実行でき、新しいブラウザは「クラシック」JavaScriptで古いコードを実行できます。スクリプト"use asm";プロローグがない場合、「古い方法で」実行されます。

Asm.jsの例
C++, , Emscripten:

 function __ZL5sievePbi($workvec,$vecsize) { $workvec = $workvec|0; //      |0 $vecsize = $vecsize|0; var $0 = 0, $1 = 0, $10 = 0, $11 = 0, $12 = 0, $13 = 0, $14 = 0, $15 = 0, $16 = 0, $17 = 0, $18 = 0, $19 = 0, $2 = 0, $20 = 0, $21 = 0, $22 = 0, $23 = 0, $24 = 0, $25 = 0, $26 = 0; var $27 = 0, $28 = 0, $29 = 0, $3 = 0, $30 = 0, $31 = 0, $32 = 0, $33 = 0, $4 = 0, $5 = 0, $6 = 0, $7 = 0, $8 = 0, $9 = 0, $i = 0, $i1 = 0, $j = 0, label = 0, sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 32|0; if ((STACKTOP|0) >= (STACK_MAX|0)) abort(); $0 = $workvec; $1 = $vecsize; $2 = $0; HEAP8[$2>>0] = 0; //       >>0 $3 = $0; $4 = ((($3)) + 1|0); //      HEAP8[$4>>0] = 0; $i = 2; while(1) { $5 = $i; $6 = $1; $7 = ($5|0)<($6|0); //     if (!($7)) { break; } $8 = $i; $9 = $0; $10 = (($9) + ($8)|0); HEAP8[$10>>0] = 1; $11 = $i; $12 = (($11) + 1)|0; $i = $12; } $i1 = 2; while(1) { $13 = $i1; $14 = $1; $15 = ($13|0)<($14|0); if (!($15)) { break; } $16 = $i1; $17 = $0; $18 = (($17) + ($16)|0); $19 = HEAP8[$18>>0]|0; //     $20 = $19&1; L8: do { if ($20) { $21 = $i1; $22 = $21<<1; $j = $22; while(1) { $23 = $j; $24 = $1; $25 = ($23|0)<($24|0); if (!($25)) { break L8; } $26 = $j; $27 = $0; $28 = (($27) + ($26)|0); HEAP8[$28>>0] = 0; $29 = $j; $30 = $i1; $31 = (($29) + ($30))|0; $j = $31; } } } while(0); $32 = $i1; $33 = (($32) + 1)|0; $i1 = $33; } STACKTOP = sp;return; } 

興味深いことに、開発中のasm.jsがJavaScriptの制限(64ビット数、マルチスレッド、SIMD命令などのサポートの欠如)に「遭遇」した場合、欠落している構造がJavaScript標準に追加されました。したがって、asm.jsと標準JavaScriptとの互換性は、asm.js開発者だけでなく、言語標準開発者の努力の結果です。

すでに述べた理由に加えて、Javaアプレットを使用してLLVMに基づく新しいテクノロジーを使用する代わりに、asm.jsサポーター次の引数を提供します。「JavaScriptには仮想マシンが組み込まれているため、もう1つ追加すると、接続APIの2番目のセットが表示され、DOM、ネットワーク、センサー、入力デバイスなどに仮想マシンがアクセスできるようになります。このために何かを犠牲にしなければなりません。たとえば、仮想マシンのプロセスは、利用可能なリソースをどのように分散しますか?この質問に答えるのは思ったより難しいです。」 (オリジナルのスペル)特に、これはエンジンの新機能がasm.jsと「クラシック」JavaScriptから同時に利用できることを意味します。2回実装する必要はありません。

未来


GoogleとAppleが依存していた生のLLVM-IRと比較して、asm.jsには多くの利点があります。しかし、欠点もあります。まず、asm.jsのコードのサイズは非常に大きくなっています。2つの整数を追加すると20バイト以上かかります。第二に、asm.jsコードを実行するには、再解析する必要があります。JavaScript構文を使用してコードからコードを生成し、このコードからASTを復元するのではなくASTの形式でコンパイルの結果を保存する方がはるかに効率的です。そのため、2015年の夏に、WebAssemblyという新しい「ユニバーサルマシン言語」が登場しました。asm.jsの長所の一部(JavaScriptエンジンとの統合など)を保持し、2つの欠点を解消し、古いJavaScriptエンジンとの直接の互換性を拒否しました(vsbのコメントまたは、ブラウザでゲームがまったく起動しなかったため、または、1 fpsで始まりました-違いはありません、とにかく再生できません。」)代わりに、WebAssembly開発者は「パテ」 -WebAssemblyでコードを読み取り、その場でコードを生成するJavaScriptライブラリ古典的な「asm.js; そして彼の古いバージョンのブラウザはすでに実行方法を知っています。

WebAssemblyの例
C++, , WebAssembly LLVM:

_Z5sievePbi:
	.param i32 #    $0  $1
	.param i32
	.local i32, i32, i32, i32 #    $2, $3, $4, $5
# %entry
	block BB0_9 #       --   
	i32.const $2, 0 #     
	i32.store8 $0, $2 #     
	i32.const $3, 1
	i32.add $push0, $0, $3 #    --  
	i32.store8 $pop0, $2 #     
	i32.const $4, 2
	set_local $5, $4
BB0_1: # %for.cond
	loop BB0_3
	i32.ge_s $push1, $5, $1 #    -- 
	br_if $pop1, BB0_3
# %for.body
	i32.add $push7, $0, $5 # $pushN  $popN -- ""  
	i32.store8 $pop7, $3 #    ( )
	i32.add $5, $5, $3 #     
	br BB0_1
BB0_3: # %for.cond.4
	loop BB0_9
	block BB0_8
	block BB0_4
	block BB0_3 #    ,   (? ?)
	i32.lt_s $push2, $4, $1
	br_if $pop2, BB0_4
	br BB0_9
BB0_4: # %for.body.7
	i32.add $push3, $0, $4
	i32.load8_u $5, $pop3 #       $4
	i32.eq $push4, $5, $2 #    
	br_if $pop4, BB0_8
# %if.then
	i32.shl $5, $4, $3 #  $4   , .. 
BB0_6: # %while.cond
	loop BB0_8
	i32.ge_s $push5, $5, $1
	br_if $pop5, BB0_8
# %while.body
	i32.add $push6, $0, $5
	i32.store8 $pop6, $2
	i32.add $5, $5, $4
	br BB0_6
BB0_8: # %for.inc.11
	i32.add $4, $4, $3
	br BB0_3
BB0_9: # %for.cond.cleanup.6
	return

LLVM-IR IA-32, ; WebAssembly , .

, , VEG - — « WebAssembly .» — .

GoogleはWebAssemblyの主要な開発者の1つになりました-LLVMに基づいた新しい「ユニバーサルマシン言語」では、PNaClの開発中に得られたすべての経験が考慮されます。彼らは自分でPNaClを開発するつもりはありません。もう1つの主要な開発者であるMozillaは、asm.jsとは異なり、WebAssemblyが構文的にも意味的にも特定のPLに接続されていないことを強調しています。 10年か2年でJavaScriptが忘却の対象になり、ブラウザーはソースコードのスクリプトをサポートしなくなります。代わりに、WebAssemblyはブラウザーのWebアプリケーションのネイティブ形式になります。ソフトウェア開発者が複数ページのリストをBASICとPascalに配布から実行可能コードの配布に切り替えた1980年代に、PCプログラムはこの「飛躍的な進歩」を遂げました。少なくとも「これをすべてコンパイルして実行するにはどうすればよいか」というレベルで、単一のプログラミング言語を知らなくてもPCを使用できるようになりました。
ほとんどのWebアプリケーションユーザーは、JavaScriptについてはまったく知りません。では、Webアプリケーションをソースコードとして配布する意味は何ですか?

モバイル機器メーカーが復活しました。ハードウェアに新しい「ユニバーサルマシン言語」でコードを実装すると、モバイルデバイスを取得するためにWebアプリケーションのパフォーマンスが大幅に向上する可能性があります。 WebAssemblyは、高レベルの機械語ですが、スタック言語ではありません。したがって、そのハードウェア実装は、スタックされたuni-ISAを実装する以前の試みよりも成功する可能性があります。最も重要なことは、x86が当時のPCの世界を変えたように、すべてのモバイルアプリケーション用の単一の機械語でエコシステム全体を変えることができることです。 「x86覇権」以前は、PC向けのプログラムは、さまざまなプラットフォームで多かれ少なかれ互換性のあるバージョンでリリースされていました。たとえば、Prince of Persiaは、Amiga、Amstrad、Apple II、Atari、FM Towns、IBM PC、Macintosh Quadra、NEC PC-9801、SAMCoupé、Sharp X68000。しかし、ある時点から「PC用プログラム」というフレーズは「x86用プログラム」を意味し始めました。同様に、「単一のモバイルISA」は、ソフトウェア開発者、デバイスメーカー(OEM)、およびプロセッサメーカーのリンクを解除します。OEMは、デバイスに任意のメーカーのプロセッサを選択できます。メーカー;開発者は、異なるプラットフォーム間でアプリケーションを転送するためにお金を費やす必要がなくなります。最終的に、必要なプログラムのいずれかが自分のデバイスで起動されると、ユーザーが勝ちます。同様に、「単一のモバイルISA」は、ソフトウェア開発者、デバイスメーカー(OEM)、およびプロセッサメーカーのリンクを解除します。OEMは、デバイスに任意のメーカーのプロセッサを選択できます。メーカー;開発者は、異なるプラットフォーム間でアプリケーションを転送するためにお金を費やす必要がなくなります。最終的に、必要なプログラムのいずれかが自分のデバイスで起動されると、ユーザーが勝ちます。同様に、「単一のモバイルISA」は、ソフトウェア開発者、デバイスメーカー(OEM)、およびプロセッサメーカーのリンクを解除します。メーカー;開発者は、異なるプラットフォーム間でアプリケーションを転送するためにお金を費やす必要がなくなります。最終的に、必要なプログラムのいずれかが自分のデバイスで起動されると、ユーザーが勝ちます。開発者は、異なるプラットフォーム間でアプリケーションを転送するためにお金を費やす必要がなくなります。最終的に、必要なプログラムのいずれかが自分のデバイスで起動されると、ユーザーが勝ちます。開発者は、異なるプラットフォーム間でアプリケーションを転送するためにお金を費やす必要がなくなります。最終的に、必要なプログラムのいずれかが自分のデバイスで起動されると、ユーザーが勝ちます。

一方、単一の機械語はプロセッサメーカーに厳格なフレームワークを課します。「シングルISA」に受け入れられ、他のメーカーによって実装されるまで、新しい機能を新しいプロセッサに追加することはできません。さらに、統一された機械語の開発により、メーカーの1つが言語に特に有益な機能を言語にプッシュしようとすると、状況は避けられません。たとえば、Intelプロセッサの整数除算命令は、ゼロで除算するときに例外をスローします。 ARMプロセッサで-結果としてゼロを返します。ARMエンジニアは、除​​数テストをコンパイラにシフトし、除数が意図的にゼロでない場合に除算を高速化する方が良いと考えました。第三に、グラフィックスのサポートはどうですか? WebAssembly は試してさえいませんGPUに対して単一のISAを提供します-異なるメーカーの好みの違いは、IntelとARMのコマンドシステム間よりもさらに大きくなります。

ただし、これらの考慮事項はすべて、WebAssemblyが根付くという事実に基づいており、PNaClのように数年後には放棄されず、JavaやMSILのような別のニッチで隔離されることはありません。

見てみましょう。

Source: https://habr.com/ru/post/J270379/


All Articles