owned this note
owned this note
Published
Linked with GitHub
###### tags: `RCWT` `VM`
# 仮想マシンとは
## 処理系
処理系とは、プログラミング言語の構文や意味を解析し実行するものである。この処理は複雑で、特に速度やメモリなどの最適化を加えると、より煩雑な処理になる。そのため、この処理は伝統的に以下のように分割して考える。
- フロントエンド
- 字句解析
- 構文解析
- 意味解析
- 最適化
- バックエンド(ランタイム)
## コンパイラとインタプリタ
コンパイラとインタプリタには厳密な定義は存在しない(と思う)。コンパイラとは、しばしばフロントエンドが肥大化し、最適化やライブラリといった機能を内包するものを指す。主に、コンパイラはアセンブリを介し、CPUの直接扱う機械語やそれに近いレイヤの言語を出力とする。対してインタプリタとは、高級言語のループや再帰がバックエンドとなることが多い(高級言語を出力とする可能性もある)。
上記の理由で、コンパイラは機械依存の最適化をでき時間的にも空間的にも効率が良く、インタプリタは機械にあまり依存せずプログラムの移植性が高い。
## 仮想マシン
ここでいう仮想マシンとはプロセス仮想マシンと呼ばれるものである。これは分類するならインタプリタのバックエンドにあたる。命令の列を受け取り、CPUのようにレジスタまたはスタックでこれを処理する。高級言語で実現された"仮想の"CPUというわけである。
他のインタプリタとの違いは、効率である。インタプリタによくみられる、構文木を辿ったりループで命令を処理したりする実装は、時間的にも空間的にも効率が悪い。
```rust
// (構文木)トラバーサ: 構文木を辿る
enum Node {
Add(Box<Node>, Box<Node>),
Sub(Box<Node>, Box<Node>),
Mul(Box<Node>, Box<Node>),
Div(Box<Node>, Box<Node>),
Num(isize),
}
impl Node {
fn execute(&self) -> isize {
match *self {
Add(l, r) => l.execute() + r.execute(),
Sub(l, r) => l.execute() - r.execute(),
Mul(l, r) => l.execute() * r.execute(),
Div(l, r) => l.execute() / r.execute(),
Num(n) => n,
}
}
}
```
```rust
// インタプリタループ: ループで命令を処理する
struct Instr {
Add, Sub, Mul, Div, Num(isize),
}
fn execute(instrs: &Vec<Instr>) -> isize {
let mut stack: Stack<isize> = Stack::new();
for instr in instrs {
match *instr {
Add => {
let right = stack.pop();
let left = stack.pop();
stack.push(left + right);
},
Sub => {
let right = stack.pop();
let left = stack.pop();
stack.push(left - right);
},
Mul => {
let right = stack.pop();
let left = stack.pop();
stack.push(left * right);
},
Div => {
let right = stack.pop();
let left = stack.pop();
stack.push(left / right);
},
Num(n) => {
stack.push(n);
},
}
}
stack.pop()
}
```
## コンパイラ(Compiler)
### フロントエンド(Front End)
- 字句解析(Lexical Analysis)
- 構文解析(Syntax Analysis)
- 抽象構文(Abstract Syntax)
- 意味解析(Semantics Analysis)
### ミドルエンド(Middle End)
- 内部表現(Internal Representation)
- レジスタ割付
- 最適化(Optimization)
### バックエンド(Back End)
- 仮想マシン(Virtual Machine)
A **virtual machine(VM)** is an emulation of a computer system. VMs are based on computer architectures and provide functions of a real computer. There is different kinds of VMs, each with different purposes:
- system VM
- process VM
I'm going to talk about process VM. A process VM, sometimes called Runtime Environment(RE), runs as a normal application inside a host OS and supports a single process. Its purpose is to provide a **platform-independent** programming environment that abstracts away details of the underlying hardware or operating system and allows a program to execute in the same way on any platform. Process VMs are implemented using an **interpreter**; performance comparable to compiled programming languages can be achieved by the use of **just-in-time compilation**.This type of VM has become popular with the Java programming language, which is implemented using the Java virtual machine(JVM).
```cpp
// direct threading
#define INIT_DISPATCH JUMP;
#define CASE(op) L_ ## op:
#define NEXT i=*++pc; goto *table[i.type]
#define JUMP i=*pc; goto *table[i.type]
#define END_DISPATCH L_END:
auto* pc = entry_point;
instruction i = *new instruction;
static void* table[] = {
/* 00 */ &&L_NOP
...
};
INIT_DISPATCH {
CASE(NOP) {
} NEXT;
...
} END_DISPATCH;
```
This is aa example of a simple process VM. `NOP` is one of its instructions. It includes an interpreter loop(`INIT_DISPATCH..END_DISPATCH`), which is direct threaded. In General, interpreter loop is less efficient but more platform-independent than processors of compiled program and direct threading is one of what makes interpreter loop more efficient. It replaces `while` and `switch` statements with `goto` statements.
- ガベージコレクション
- JITコンパイル
### ABI(dolce氏)
- RCWTを接頭辞とするのが正確だが読みづらいので<u>vまたはvirtual</u>を接頭辞とする
- プラットフォーム関連機能は<u>nまたはnative</u>を接頭辞とする
```
# 疑似コード(逆アセンブル)
numRegs = 6
numDefs = 1
numUndefs = 0
numRelocs = 0
text
main {
add r1 r0 1 # r1 = r0 + 1
add r2 r0 2 # r2 = r0 + 2
add r3 r0 3 # r3 = r0 + 3
mul r4 r2 r3 # r4 = r2 * r3
add r5 r1 r4 # r5 = r1 + r5
iout r5
add r6 r0 0 # r6 = r0 + 0
sout r6
}
data
s0 = "Hello, World!"
```
- ブログにVMのことをまとめる
- BP命令のとき処理を中断(INT 0x03)
- 命令/データの挿入削除(検索)
- (スレッドの監視)
- (wc/dll/soのダイナミックロード)
- (エラーハンドラ)
- (バックドア)
- (即値オーバーフローの検出)
### タスク
- 排他的なメモリを持つ
- 互いに非同期通信できる(q)
ポインタから参照へ
debug.cppだけ差し替えれば独自のデバッガが使える
mainでqを作ってコアとデバッグで共有(qにはenvのコピーを流す)
bpの停止はコアが担当
lock free/atomic