プログラミング言語処理系論 (8)
Design and Implementation of
Programming Language Processors
佐藤周行
(情報基盤センター/電気系専攻融合情報学
コース)
今日の予定
VM設計の実際(前回とすこし被る)
Stack Machine vs. Register Machine
命令セットアーキテクチャ
ターゲットとなるプログラミング言語を効率よく実行す
るために
Java VM vs. Parrot
parrot.org/
Stack Machine vs. Register
Machine
Stack Machine
各種演算(load/store, arithmetic, …)のオペ
ランドにスタックを想定する
スタックに「プッシュする」「ポップする」演算が
explicit/implicitにある
フレームなど、スタックで効率よく実装できるもの
が多くある
コードが簡潔になる場合が多い
Register Machine
Register Machine
各種演算のオペランドにレジスタ(変数)を想定す
る
ハードウェア命令との親和性が高いと主張する
ハードウェアとの親和性を考えるとき、命令体系のレ
ジスタセットとハードウェアのレジスタセットが異なる
場合、「Register Allocation」が必要になる
レジスタの選択の自由度が上がる分、コードは一
般に複雑
ではJava VMをみてみましょう
命令セットアーキテクチャ (ISA)
VMで扱うデータの型について
対象となる言語によって若干違い
命令をデザインする時に必要
JAVA VM
byte/char/short/int/long
float/double
boolean/return address
reference (class/array/interface)
ISA
Parrot
I (Integer)
N (Real)
S (String)
P (Polymorphic)
それぞれの型に応じてレジスタが用意されて
いる
Ixx, Nxx, Sxx, Pxx
言語的に重要なもの
オブジェクトの生成
new
関数(メソッド)コール
フレームの構築
invokevirtual
invokestatic
invokespecial
invokedynamic
コンパイルしてみる
class x {
static int content;
x(int y) {
content = y;
}
public int get_content() {
return content;
}
public void addxx(x x1) {
int y;
}
}
フレーム
フレーム0
フレーム1
フレーム2
フレーム3
this
第一引数
第二引数
第三引数
0
y = x1.get_content();
content += y;
iload_n, aload_n, istore_n, aload_n等の命令が
フレーム内のデータにアクセスするために用意されている
コンパイル結果
class x {
static int content;
x(int);
Code:
0: aload_0
1: invokespecial #1
// Method
java/lang/Object."<init>":()V
4: iload_1
5: putstatic
#2
8: return
public int get_content();
Code:
0: getstatic
#2
3: ireturn
}
public void addxx(x);
Code:
0: aload_1
1: invokevirtual #3
// Method get_content:()I
4: istore_2
5: getstatic
#2
// Field content:I
8: iload_2
9: iadd
10: putstatic
#2
// Field content:I
13: return
ターゲット言語の重要な要素
オブジェクト指向言語
オブジェクト
Method invocation
フレームは普通にスタックに積むのだが、Java VMで
はそこまでは強制されていない
スクリプト言語
動的型付け
文字列の操作が…
Parrot
Perl6用のVM
多くの動的型付け言語対応を唱う
では、Parrotのうたい文句を見てみましょう
Parrot is a language-neutral virtual machine for
dynamic languages such as Ruby, Python, PHP,
and Perl. It hosts a powerful suite of compiler
tools tailored to dynamic languages and a next
generation regular expression engine.
少しambitiousなことが書いてある
Its architecture is fundamentally
different than existing virtual
machines such as the JVM or CLR,
with optimizations for dynamic
languages included, a register-based
system rather than stack-based, and
the use of continuations as the core
means of flow control.
構成
Parrot
VM
言語として
PIR
PASM
中間言語系
アセンブリ言語
Parrotの構造
Perl他ターゲット言語
PIR
PASM
PBC
Parrot
では、Parrotの解析
データ型は
Integer
Number
String
PMC (Parrot Magic Cookie)
Register Machine
Registerとして
Ixx
Nxx
Sxx
Pxx
変数はスタックに積むことなく、「レジスタ名」で
アクセスされる
簡単な例(ただし分厚いライブラリ付)
.loadlib 'io_ops'
.pcc_sub :main main:
getstdin P0
getstdout P1
REDO:
readline S0, P0
print S0
if S0, REDO
end
Parrotに求められるものは何か?
「オブジェクト」に対する動的な型付けのサポ
ート。そのためにPMCを用意した
PMC
Scalar/array/subroutine/namespace を収
容できる
目的に応じて柔軟に型変換が可能になっている
PMCのタイプ
Env
Iterator
Array
Hash
String
Integer
Float
Exception
Timer
たとえば、これはどうか(動的関係ない
.sub 'example' :main
$I1 = 96
$I2 = 64
print "Algorithm E (Euclid's algorithm)¥n"
e1:
$I4 = mod $I1, $I2
e2:
unless $I4 goto done
e3:
$I1 = $I2
$I2 = $I4
branch e1
done: print "The greatest common denominator of 96 and 64 is "
print $I2
print ".¥n"
.end
ではこちらはどうか
$i = 4.3;
print fact();
sub fact {
$r = 1;
while ($i > 0) {
$r = $r * $i;
$i--;
}
return $r;
}
解決方法
特定の演算を指定して、polymorphicにする
この場合はdecrement, multiply
PMCを引数に取る時の挙動を定める
→ VTABLEで処理内容を指定する
型指定のタイミングを変数の代入の時点で制御で
きるようにする
代入のsemanticsを定める
実際 src/ops/math.opsでは
inline op dec(inout INT) {
$1--;
}
inline op dec(inout NUM) {
$1--;
}
inline op dec(invar PMC) {
VTABLE_decrement(interp, $1);
}
ISA
まずは命令セットを眺めてみましょうか
src/include/oplib/ops.h
1124個の命令
高レベルのものを含む(特に制御関係)
ISA
特に代入(set)を見てみましょう
inline op set(out INT, in INT) {
$1 = $2;
}
inline op set(out INT, in NUM) {
$1 = (INTVAL)($2);
}
inline op set(out INT, in STR) {
$1 = Parrot_str_to_int(interp, $2);
}
inline op set(out NUM, in NUM) {
$1 = $2;
}
…
Non-trivialなsetは…
inline op set(invar PMC, in INT) {
VTABLE_set_integer_native(interp, $1, $2);
}
inline op set(invar PMC, in NUM) {
VTABLE_set_number_native(interp, $1, $2);
}
inline op set(invar PMC, invar STR) {
VTABLE_set_string_native(interp, $1, $2);
}
inline op set(invar PMC, inconst STR) {
VTABLE_set_string_native(interp, $1, $2);
}
inline op set(out INT, invar PMC) {
$1 = VTABLE_get_integer(interp, $2);
}
Set
全体として以下のパターンに対してそれぞれ
対応する関数が定められている
set (単純なパターン)
set P {I/N/S}
set Px [key] By/ set Ax Py[key]
白鳥は優雅に泳いでいるが…というパターン
関数呼び出しについて(PIR)
まずはフレーム
# factorial.pir
.sub 'main' :main
.local int count
.local int product
count = 5
product = 1
$I0 = 'fact'(count,
product)
say $I0
.end
.sub 'fact'
.param int c
.param int p
loop:
if c <= 1 goto fin
p=c*p
dec c
branch loop
fin:
.return (p)
.end
関数コール
Signature で型付け
IN -> OUT
関数コールごとにcontextを作成(特別のスタ
ックがあるわけではない)
Continuationを受け渡すことができる
Coroutine等が実現可能…
名前解決
後は、グローバルな変数(名前でしかアクセス
できないデータ)の扱いですが
以下の命令が用意されている
{store/find}_lex 名前付き変数の解決
get_namespace をやる前提で
get_global
set_global
find_name
© Copyright 2026 ExpyDoc