p-kod makinesi - p-code machine

Olarak bilgisayar programlama , bir p-kod makinesi ( taşınabilir kod makine ) a, sanal makine çalıştırmak için tasarlanmış , p-kodu ( montaj dili ya da makine kodu varsayımsal bir merkezi işlem birimi (CPU)). Bu terim hem genel olarak tüm bu tür makinelere ( Java sanal makinesi (JVM) ve MATLAB önceden derlenmiş kodu gibi) hem de en ünlüsü Pascal-P sisteminin p-Makinesi , özellikle UCSD Pascal olan belirli uygulamalara uygulanır. Geliştiriciler arasında, p-kodundaki p'nin , taşınabilirden daha sık sözde anlamına geldiği , dolayısıyla sözde kod , bir sözde makine için talimatlar anlamına gelen uygulama.

Konsept ilk olarak 1966 dolaylarında ( Temel Kombine Programlama Dili ( BCPL ) için O-kodu ve Euler dili için P kodu olarak ) uygulanmış olsa da, p-kodu terimi ilk olarak 1970'lerin başında ortaya çıktı. P kodu üreten iki erken derleyici , 1973'te Kesav V. Nori, Urs Ammann, Kathleen Jensen, Hans-Heinrich Nägeli ve Christian Jacobi tarafından Pascal-P derleyicisi ve 1975'te Niklaus Wirth tarafından Pascal-S derleyicisiydi .

p-koduna çevrilen programlar ya varsayımsal CPU'nun davranışını taklit eden bir yazılım programı tarafından yorumlanabilir ya da programın üzerinde çalışacağı ve daha sonra yürütüleceği CPU'nun makine koduna çevrilebilir . Yeterli ticari ilgi varsa, CPU spesifikasyonunun bir donanım uygulaması oluşturulabilir (örneğin, Pascal MicroEngine veya bir Java işlemci sürümü ).

p-kodunu uygulamanın yararları ve zayıflıkları

Yerel makine koduna doğrudan çeviri ile karşılaştırıldığında , p-koduna çeviri ve yorumlayarak veya tam zamanında derleme (JIT) ile yürütmeyi içeren iki aşamalı bir yaklaşım çeşitli avantajlar sunar.

  • Yeni bir makine için küçük bir p kodu yorumlayıcısı yazmak, bir derleyiciyi aynı makine için yerel kod üretecek şekilde değiştirmekten çok daha kolaydır.
  • Makine kodu oluşturmak, derleyici yazmanın daha karmaşık kısımlarından biridir. Karşılaştırıldığında, bayt kodunu oluştururken makineye bağlı hiçbir davranışın dikkate alınmaması gerektiğinden, p kodu oluşturmak çok daha kolaydır . Bu, bir derleyiciyi hızlı bir şekilde kurmak ve çalıştırmak için kullanışlı hale getirir.
  • p-kodu ideal bir sanal makineyi temel aldığından, bir p-kodu programı genellikle makine koduna çevrilen aynı programdan çok daha küçüktür.
  • p kodu yorumlandığında, yorumlayıcı yerel kodla uygulanması zor olan ek çalışma zamanı kontrolleri uygulayabilir.

P-kodunun önemli dezavantajlarından biri, bazen JIT derlemesi yoluyla giderilebilen yürütme hızıdır. P kodunun tersine mühendislik işlemi yerel koddan daha kolaydır .

1980'lerin başında, en az iki işletim sistemi , p-kodunun kapsamlı kullanımıyla makine bağımsızlığını elde etti . İş İşletim Sistemi (BOS) münhasıran p-code programları çalıştırmak için tasarlanmış bir çapraz platform işletim sistemi oldu. The University of California, San Diego'da geliştirilen UCSD p-System , Pascal dili tarafından nesil için optimize edilmiş p-kodunu temel alan, kendi kendini derleyen ve kendi kendini barındıran bir işletim sistemiydi .

1990'larda, p-koduna çeviri gibi dillerin uygulamaları için popüler bir strateji haline Python , Microsoft, P-Code içinde Visual Basic ve Java byte içinde Java .

Go dili , Ken Thompson tarafından Bell Labs'den Plan 9 üzerindeki çalışmanın bir uzantısı olarak uygulanan bir p-kodu biçimi olarak genel, taşınabilir bir derleme kullanır . Aksine ortak dil çalışma zamanı (CLR) bayt veya JVM bayt, hiçbir istikrarlı şartname yoktur ve bir bayt kodu biçimi yaymaz Git inşa etmek araçları daha sonra kullanılmak üzere. Go assembler, bir ara temsil olarak genel derleme dilini kullanır ve Go yürütülebilir dosyaları, makineye özgü statik olarak bağlantılı ikili dosyalardır.

UCSD p-Makine

Mimari

Diğer birçok p-kod makinesi gibi, UCSD p-Machine bir yığın makinesidir , bu da çoğu talimatın işlenenlerini bir yığından aldığı ve sonuçları yığına geri yerleştirdiği anlamına gelir . Böylece addtalimat, yığının en üstteki iki öğesini toplamlarıyla değiştirir. Birkaç talimat hemen bir argüman alır. Pascal gibi, p-kodu da güçlü bir şekilde yazılır , boolean (b), karakter (c), tamsayı (i), gerçek (r), set (s) ve işaretçi (a) veri türlerini doğal olarak destekler.

Bazı basit talimatlar:

Insn.   Stack   Stack   Description
        before  after
 
adi     i1 i2   i1+i2   add two integers
adr     r1 r2   r1+r2   add two reals
inn     i1 s1   is1     set membership; b1 = whether i1 is a member of s1
ldi     i1 i1   i1      load integer constant
mov     a1 a2   a2      move
not     b1 b1   -b1     boolean negation

Çevre

Diğer yığın tabanlı ortamlardan ( Forth ve Java sanal makinesi gibi ) farklı olarak, ancak gerçek bir hedef CPU'ya çok benzer, p-Sistemi, prosedür yığın çerçeveleri ( dönüş adresi vb. sağlayan) tarafından paylaşılan yalnızca bir yığına ve bağımsız değişkenlere sahiptir. yerel talimatlar. Makinenin kayıtlarından üçü yığına işaret eder (yukarı doğru büyür):

Ayrıca sabit bir alandır ve bunun altında yığın yığına doğru büyür. NP ( yeni işaretçi ) kaydı, yığının en üstüne (en düşük kullanılan adres) işaret eder. EP, NP'den büyük olduğunda, makinenin belleği tükenir.

Beşinci kayıt olan PC, kod alanındaki mevcut talimatı işaret eder.

çağrı kuralları

Yığın çerçeveleri şöyle görünür:

EP ->
      local stack
SP -> ...
      locals
      ...
      parameters
      ...
      return address (previous PC)
      previous EP
      dynamic link (previous MP)
      static link (MP of surrounding procedure)
MP -> function return value

Prosedür çağırma sırası aşağıdaki gibi çalışır: Çağrı,

 mst n

burada nyuvalama seviyelerindeki farkı belirtir (Pascal'ın yuvalanmış prosedürleri desteklediğini unutmayın). Bu talimat yığını işaretler , yani yukarıdaki yığın çerçevesinin ilk beş hücresini rezerve eder ve önceki EP, dinamik ve statik bağlantıyı başlatır. Arayan daha sonra prosedür için herhangi bir parametreyi hesaplar ve gönderir ve ardından

 cup n, p

bir kullanıcı prosedürünü çağırmak için ( nparametre sayısı p, prosedürün adresi). Bu, bilgisayarı iade adres hücresine kaydedecek ve prosedürün adresini yeni bilgisayar olarak ayarlayacaktır.

Kullanıcı prosedürleri iki talimatla başlar

 ent 1, i
 ent 2, j

Birincisi SP'yi MP+' iya, ikincisi EP'yi SP+'ya ayarlar j. Yani iesasen yereller için ayrılmış alanı belirtir (artı parametre sayısı artı 5) ve jyığın için yerel olarak gereken giriş sayısını verir. Bu noktada bellek tükenmesi kontrol edilir.

Arayana geri dönüş şu şekilde gerçekleştirilir:

 retC

ile C(geri dönüş değeri için, yukarıdaki gibi, I, R, C, B, ve p) dönüş vermenin. Dönüş değeri önceden uygun hücrede saklanmalıdır. p hariç tüm türlerde, döndürme bu değeri yığında bırakacaktır.

Bir kullanıcı prosedürü (cup) çağırmak yerine, standart prosedür qile çağrılabilir.

 csp q

Bu standart prosedürler readln()( csp rln), sin()( csp sin), vb. Pascal prosedürleridir . Tuhaf eof()bir şekilde bunun yerine bir p-Kodu talimatıdır.

Örnek makine

Niklaus Wirth, 1976 tarihli Algorithms + Data Structures = Programs kitabında basit bir p-kod makinesi tanımladı . Makinenin 3 kaydı vardı - bir program sayacı p , bir temel kayıt b ve bir yığın üstü kayıt t . 8 talimat vardı:

  1. yanıyor 0, a  : yük sabiti a
  2. Opr 0, bir  : yürütme işlemi bir (13 işlemleri: GERİ, 5 matematik fonksiyonları ve 7 karşılaştırma fonksiyonları)
  3. lod l , a  : yük değişkeni l,a
  4. sto l , a  : l,a değişkenini depola
  5. cal l , a  : çağrı usulü bir seviyesinde l
  6. int 0, a  : t kaydını a ile artır
  7. JMP 0, bir  : atlama a
  8. JPC 0, bir  : atlama koşullu için bir

Pascal ile yazılmış makinenin kodu:

const
	amax=2047;      {maximum address}
	levmax=3;       {maximum depth of block nesting}
	cxmax=200;      {size of code array}

type 
	fct=(lit,opr,lod,sto,cal,int,jmp,jpc);
	instruction=packed record 
		f:fct;
		l:0..levmax;
		a:0..amax;
	end;

var
	code: array [0..cxmax] of instruction;

procedure interpret;

  const stacksize = 500;

  var
    p, b, t: integer; {program-, base-, topstack-registers}
    i: instruction; {instruction register}
    s: array [1..stacksize] of integer; {datastore}

  function base(l: integer): integer;
    var b1: integer;
  begin
    b1 := b; {find base l levels down}
    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 {generate new block mark}
            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};

Bu makine, derleyici geliştirmeyi öğretmek için kullanılan bir Pascal alt küme derleyicisi olan Wirth's PL/0'ı çalıştırmak için kullanıldı.

Ayrıca bakınız

Referanslar

daha fazla okuma