コンピュータプログラミングにおいて、Pコードマシン(ポータブルコードマシン[1])は、仮想の中央処理装置(CPU)のアセンブリ言語またはマシンコードであるPコードを実行するように設計された仮想マシンです。「Pコードマシン」という用語は、そのようなマシン(Java仮想マシン(JVM)やMATLABのコンパイル済みコードなど)すべて、およびそれらのマシンを使用した特定の実装に一般的に適用されます。Pコードマシンの最も注目すべき用途の1つは、Pascal-PシステムのPマシンです。このシステム内のUCSD Pascal実装の開発者は、PコードのPをポータブルよりも疑似を意味すると解釈することが多く、疑似マシンの命令を意味する 疑似コードに独自のラベルを採用しました。
この概念は、1966年頃にBasic Combined Programming Language ( BCPL )のOコードとEuler言語のPコードとして初めて実装されましたが、[2] Pコードという用語は1970年代初頭に初めて登場しました。Pコードを生成する初期のコンパイラとしては、1973年にKesav V. Nori、Urs Ammann、Kathleen Jensen、Hans-Heinrich Nägeli、Christian Jacobiが開発したPascal-Pコンパイラ[3]と、1975年にNiklaus Wirthが開発したPascal-Sコンパイラの2つがあります。
P コードに変換されたプログラムは、仮想 CPU の動作をエミュレートするソフトウェア プログラムによって解釈されるか、プログラムが実行される CPU のマシン コードに変換されて実行されます。十分な商業的関心があれば、CPU 仕様のハードウェア実装が構築される可能性があります (例: Pascal MicroEngineまたはJava プロセッサのバージョン)。
Pコードとマシンコード
一般的なコンパイラモデルはプログラム コードをマシン コードに変換することを目的としていますが、P コード マシンの考え方は、P コードへの変換と、 P コード マシンによる解釈またはジャストインタイム コンパイル(JIT) による実行という 2 段階のアプローチに従います。
この分離により、P コードインタープリタの開発を、バイトコードを生成する際にマシン依存の動作を考慮する必要がある基礎となるマシン コード コンパイラから切り離すことができます。この方法では、P コード インタープリタをより迅速に実装することもでき、実行時にコードを解釈する機能により、ネイティブ コードでは同様に利用できない追加の実行時チェックが可能になります。さらに、P コードは理想的な仮想マシンに基づいているため、P コード プログラムは、マシン コードに変換された同じプログラムよりも小さくなることがよくあります。逆に、P コード ベースのプログラムの 2 段階の解釈は実行速度の低下につながりますが、これはジャストインタイム コンパイルで対処できる場合があり、構造が単純なため、ネイティブ コードよりもリバース エンジニアリングが容易です。
Pコードの実装
1980 年代初頭、少なくとも 2 つのオペレーティング システムがP コードを広範に使用してマシン独立性を実現しました[要出典]。ビジネス オペレーティング システム(BOS) は、P コード プログラムのみを実行するように設計されたクロスプラットフォームオペレーティング システムでした。カリフォルニア大学サンディエゴ校で開発されたUCSD p-System は、 Pascal言語による生成に最適化された P コードに基づく、自己コンパイルおよび自己ホスティングのオペレーティング システムでした。
1990 年代には、 Python、Visual BasicのMicrosoft P コード、JavaのJava バイトコードなどの言語の実装において、p コードへの変換が一般的な戦略になりました。
Go言語は、ベル研究所のPlan 9の作業を拡張してケン・トンプソンが実装したpコードの一種として、汎用的で移植可能なアセンブリを使用しています。共通言語ランタイム(CLR)バイトコードやJVMバイトコードとは異なり、安定した仕様はなく、Goビルドツールは後で使用するためのバイトコード形式を出力しません。Goアセンブラは汎用アセンブリ言語を中間表現として使用し、Go実行ファイルはマシン固有の静的にリンクされたバイナリです。[4]
UCSD Pマシン
建築
他の多くの P コード マシンと同様に、UCSD P マシンはスタック マシンです。つまり、ほとんどの命令はスタックからオペランドを取得し、結果をスタックに戻します。したがって、add命令はスタックの最上位 2 つの要素をそれらの合計に置き換えます。いくつかの命令は即時引数を取ります。Pascal と同様に、P コードは厳密に型指定されており、ブール (b)、文字 (c)、整数 (i)、実数 (r)、セット (s)、およびポインター (a) のデータ型をネイティブにサポートします。
簡単な手順:
挿入スタック スタックの説明
前 後
adi i1 i2 i1+i2 2つの整数を加算する
adr r1 r2 r1+r2 2つの実数を加算する
inn i1 s1 b1 セットのメンバーシップ; b1 = i1 が s1 のメンバーであるかどうか
ldi i1 i1 i1 整数定数をロード
a1 a2 a2 移動
not b1 b1 -b1 ブール否定
環境
実際のターゲット CPU と同様に、P システムには、プロシージャ スタック フレーム (戻りアドレスなどを提供) とローカル命令の引数によって共有されるスタックが 1 つだけあります。マシンのレジスタのうち 3 つは、スタック (上方向に増加) を指します。
- SP はスタックの先頭 (スタック ポインタ) を指します。
- MP はアクティブなスタック フレームの先頭 (マーク ポインタ) をマークします。
- EP は、現在のプロシージャで使用されているスタックの最高位置 (エクストリーム ポインタ) を指します。
また、定数領域があり、その下にはスタックに向かって下方向に成長するヒープがあります。NP (新しいポインタ) レジスタは、ヒープの最上部 (最も低い使用アドレス) を指します。EP が NP よりも大きくなると、マシンのメモリが使い果たされます。
5 番目のレジスタ PC は、コード領域内の現在の命令を指します。
呼び出し規約
‹テンプレート マニュアルは統合が検討されています。›
スタック フレームは次のようになります。
EP ->
ローカルスタック
SP->...
地元の人々
...
パラメータ
...
返送先住所(以前のPC)
前回のEP
ダイナミックリンク(以前のMP)
静的リンク(周辺手順のMP)
MP -> 関数の戻り値
手続き呼び出しのシーケンスは次のように動作します。呼び出しは次のように導入されます。
mst n
ここで、nネストレベルの違いを指定します(Pascalはネストされたプロシージャをサポートしていることを思い出してください)。この命令はスタックをマークします。つまり、上記のスタックフレームの最初の5つのセルを予約し、以前のEP、動的、静的リンクを初期化します。次に、呼び出し側はプロシージャのパラメータを計算してプッシュし、発行します。
カップ n、p
ユーザー プロシージャを呼び出します (nはパラメータの数、pプロシージャのアドレス)。これにより、PC が戻りアドレス セルに保存され、プロシージャのアドレスが新しい PC として設定されます。
ユーザー手順は2つの手順から始まります
ent 1、i ent 2、j
最初の関数は SP を MP + に設定しi、2 番目の関数は EP を SP + に設定しますj。つまり、i基本的にはローカル用に予約されたスペース (パラメーターの数に 5 を加えた数) を指定し、jスタックにローカルに必要なエントリの数を指定します。この時点でメモリ不足がチェックされます。
発信者への返信は、
retC
戻り値の型を指定しますC(上記の i、r、c、b、a、戻り値なしの場合は p)。戻り値は、事前に適切なセルに格納されている必要があります。p 以外のすべての型では、戻るとこの値がスタックに残ります。
ユーザープロシージャ(cup)を呼び出す代わりに、標準プロシージャをq次のように呼び出すことができます。
csp q
readln()これらの標準手順は、 ( csp rln)、sin()( csp sin) などの Pascal 手順です。奇妙なことにeof()、代わりに p コード命令が使用されます。
サンプルマシン
Niklaus Wirth は、1976 年の著書「Algorithms + Data Structures = Programs」で、単純な p コード マシンを指定しました。このマシンには、プログラム カウンター p、ベース レジスタ b、スタック トップ レジスタ tの3 つのレジスタがありました。命令は 8 つありました。
lit 0, a: 負荷定数aopr 0, a: 操作aを実行します(13 個の操作: RETURN、5 個の数学関数、および 7 個の比較関数)lod l, a: 変数l、aをロードするsto l, a: 変数l、a を格納するcal l, a:レベルlでプロシージャa を呼び出すint 0, a: tレジスタをaだけ増加jmp 0, a: ジャンプjpc 0, a:条件 付きジャンプ[5]
これは Pascal で書かれたマシンのコードです:
const
amax = 2047 ; {最大アドレス} levmax = 3 ; {ブロックネストの最大深度} cxmax = 200 ; {コード配列のサイズ}
type
fct = ( lit 、opr 、lod 、sto 、cal 、int 、jmp 、jpc ) ; instruction =パックされたレコードf : fct ; l : 0 .. levmax ; a : 0 .. amax ; end ;
var
code :命令の配列[ 0..cxmax ] ;
手順を解釈する;
定数スタックサイズ= 500 ;
var
p , b , t :整数; {プログラム、ベース、トップスタックレジスタ} i :命令; {命令レジスタ} s :整数の配列[ 1 ..スタックサイズ] ; {データストア}
function base ( l : integer ) : integer ; var b1 : integer ; begin b1 := b ; {ベース l レベル下を検索} while l > 0 do begin b1 := s [ b1 ] ; l := l - 1 end ; base := b1 end {base} ;
begin
writeln ( 'start pl/0' ) ; t := 0 ; b := 1 ; p := 0 ; s [ 1 ] := 0 ; s [ 2 ] := 0 ; s [ 3 ] := 0 ; repeat i := code [ p ] ; p := p + 1 ; with i do case f of lit : begin t := t + 1 ; s [ t ] := a end ; opr : case a of {operator} 0 : begin {return} t := b - 1 ; p := s [ t + 3 ] ; b := s [ t + 2 ] ; end ; 1 : s [ t ] := - s [ t ] ; 2 : begin t := t - 1 ; s [ t ] := s [ t ] + s [ t + 1 ] end ; 3 : begin t := t - 1 ; s [ t ] := s [ t ] - s [ t + 1 ] end ; 4 : begin t := t - 1 ; s [ t ] := s [ t ] * s [ t + 1 ] end ;
5 : begin t := t - 1 ; s [ t ] := s [ t ] div s [ t + 1 ] end ; 6 : s [ t ] := ord ( odd ( s [ t ] )) ; 8 : begin t := t - 1 ; s [ t ] := ord ( s [ t ] = s [ t + 1 ]) end ; 9 : begin t := t - 1 ; s [ t ] := ord ( s [ t ] <> s [ t + 1 ]) end ; 10 : begin t := t - 1 ; s [ t ] := ord ( s [ t ] < s [ t + 1 ]) end ; 11 : begin t := t - 1 ; s [ t ] := ord ( s [ t ] >= s [ t + 1 ]) end ; 12 : begin t := t - 1 ; s [ t ] := ord ( s [ t ] > s [ t + 1 ]) end ; 13 : begin t := t - 1 ; s [ t ] := ord ( s [ t ]
<= s [ t + 1 ]) end ; end ; lod : begin t := t + 1 ; s [ t ] := s [ base ( l ) + a ] end ; sto : begin s [ base ( l ) + a ] := s [ t ] ; writeln ( s [ t ]) ; t := t - 1 end ; cal : begin {新しいブロックマークを生成} s [ t + 1 ] := base ( l ) ; s [ t + 2 ] := b ; s [ t + 3 ] := p ; b := t + 1 ; p := a end ; int : t := t + a ; jmp : p := a ; jpc : begin if s [ t ] = 0 then p := a ; t := t - 1 end end {with, case} until p = 0 ; writeln ( 'end pl/0' ) ; end {interpret} ;
このマシンは、コンパイラ開発の指導に使用されるパスカルのサブセットコンパイラであるWirthのPL/0を実行するために使用されました。 [6] [検証に失敗]
Microsoft P コード
Pコードは、マイクロソフトの独自の中間言語の名前です。これらは、マシンコードに代わるバイナリ形式を提供しました。マイクロソフトは、Pコードはパックコード[7]または疑似コード[8]の略語であると何度も述べています。
Microsoft P コードは、 Visual C++およびVisual Basicで使用されました。他の P コード実装と同様に、Microsoft P コードでは、実行速度は遅くなりますが、よりコンパクトな実行可能ファイルが可能になります。
その他の実装
参照
- バイトコード
- 中間表現
- NCRコーポレーション版Pコードマシンの設計者、ジョエル・マコーマック
- ランタイムシステム
- トークンスレッド
- シティ&ギルド 記憶コード
- プラットフォームに依存しないモデル
参考文献
- ^ Upton, Eben; Duntemann, Jeffrey; Roberts, Ralph; Mamtora, Tim; Everard, Ben (2016-09-13). Raspberry Pi でコンピューターアーキテクチャを学ぶ. John Wiley & Sons. ISBN 978-1-119-18393-8。
- ^ Wirth, Niklaus ; Weber, Helmut (1966). 「EULER: ALGOL の一般化とその正式な定義: パート II」Communications of the ACM . 9 (2). ニューヨーク、米国: Association for Computing Machinery (ACM): 89–99. doi : 10.1145/365170.365202 . S2CID 12124100.
- ^ ノリ、ケサブ V.アンマン、ウルス;ジェンセン、キャスリーン。ネーゲリ、ハンス=ハインリヒ。クリスチャン・ヤコビ (1975)。Pascal P コンパイラの実装ノート。チューリッヒ、スイス: Eidgenössische Technische Hochschule (ETH)。
- ^ Pike, Robert C. (2016). 「Goアセンブラの設計」。YouTube(カンファレンストーク)。2021年12月11日時点のオリジナルよりアーカイブ。 2017年8月25日閲覧。
- ^ 「カテゴリーアーカイブ: Wirth - Euler - Niklaus Wirth と Helmut Weber によって設計」。小型マシン向け Pascal - Wirth 言語、Pascal、UCSD、Turbo、Delphi、Freepascal、Oberon。2018-08-02。
- ^ Alpert, Donald (1979 年 9 月)。スタンフォード エミー賞のための Pascal P コード インタープリタ(PDF) (レポート)。スタンフォード大学、電気工学部およびコンピュータ科学部、コンピュータ システム研究所。技術ノート No. 164。
- ^ Padawer, Andy (1992 年 4 月)。「Microsoft P-Code Technology」。Microsoft Developer Network。2001年 2 月 22 日時点のオリジナルよりアーカイブ。
- ^ 「プロジェクトをネイティブ コードにコンパイルする」。Visual Studio 6.0 ドキュメント。2007 年。2007 年 2 月 27 日時点のオリジナルからのアーカイブ。
さらに読む
- ペンバートン、スティーブン、ダニエルズ、マーティン。Pascal実装: P4 コンパイラとインタープリタ。John Wiley。ISBN 0-13-653031-1。
- Pemberton, Steven編 (2011-04-13)。「Pascal 実装: 本とソース」(注: P4コンパイラとインタープリタの Pascal ソース、使用方法の説明があります。)
- Pemberton, Steven編 (2011-04-13)。「Pascal コンパイラ自体がコンパイルした pcode」。(注: P4コンパイラの P コードが独自に生成されています。)
- 「ジェファーソン コンピュータ ミュージアムの UCSD p-System に関するページ」
- 「オープンソース実装」。パッケージングとコンパイル済みバイナリを含む、Klebsch のフレンドリーなフォーク。「Klebsch 実装」。
- テリー、パット (2005)。C # と Java によるコンパイル。ピアソン/アディソン・ウェズリー。p. 624。ISBN 0-321-26360-X。
- Wirth, Niklaus (1975)。アルゴリズム + データ構造 = プログラム。Prentice- Hall。ISBN 0-13-022418-9。
- Wirth, Niklaus (1996)。コンパイラ構築。Addison- Wesley。ISBN 0-201-40353-6。
- Liffick, Blaise W.編 (1979)。The Byte Book of Pascal。BYTE Publications。ISBN 0-07-037823-1。
- バロン、デイビッド・ウィリアム編 (1981)。パスカル:言語とその実装。ワイリー。ISBN 0-471-27835-1。(注: 特に、 Pascal-P 実装ノートとPascal-S: サブセットとその実装の記事を参照してください。)
外部リンク
- Wayback Machineの Mr Silver による VB P コード情報(2015 年 12 月 22 日アーカイブ)
