Last Modified: 2019年7月3日

Q言語コンパイラ

yacc および lex を使用したコンパイラ作成の例として, 単純なプログラミング言語「Q言語」のコンパイラを作成した.

以下にその覚書を記す.


ソース言語 - Q言語

基本的には,C言語類似のシンタックスを持っていると考えてよい.ただし,

サンプルも参照されたい.


目的言語 - IA-32インテル®アーキテクチャのアセンブリ言語

目的言語は,IA-32インテル®アーキテクチャのアセンブリ言語である. gcc に付随しているアセンブラ (gas) で処理できるように,gas の intel 形式で出力する.そのため,出力されたコード (*.s) は通常の cc (gcc) を用いて実行ファイルが生成できる.

IA-32インテル®アーキテクチャの詳細については下記資料を参照されたい.

命令の一部を以下に簡単に示す.

.intel_syntax
インテル形式を指定する.
.macro マクロ名 引数 …
マクロ命令の定義を始める.
.endm
マクロ命令の定義を終了する.
.comm 領域名, サイズ
共有領域 (大域変数) の名前とサイズを定義する.
.ascii 文字列,
文字列リテラル(定数)を定義する.
.globl 名前
名前を大域的な名前として宣言する.
jmp ラベル
ラベルに分岐する (無条件分岐).
jnz/jz/jne/je/jge/jg/jle/jl ラベル
条件が満たされたとき,ラベルに分岐する (条件分岐).
call 手続き
手続きを呼び出す.手続きは, ラベル (ラベルそのものを飛び先とする〈直接〉) や [ラベル] (ラベルの場所のデータを番地として飛び先とする〈間接〉) や [レジスタ + 数値] (レジスタの内容に数値を足した結果の番地を飛び先とする〈インデックス〉) などがある.
ret
手続きから呼び出し元へ戻る.
mov ,
からへデータを転送する., の表現については後述.
lea ,
の実効アドレスをに格納する.
push 
の内容をスタックにプッシュする.
pop 
スタックからポップしてに転送する.
or/add/sub/imul ,
で演算(論理和/加算/減算/乗算)して,結果をへ格納する.
cdq
eax の32bit長の数値を(符号を保って)64bit長に拡張し,edx:eax に格納する.
idiv 
edx:eax とで演算(除算)して, 結果を eax へ格納する.
cmp ,
を比較して,結果をフラグレジスタへ格納する.
inc 
の内容に1を足す.
dec 
の内容から1を引く.
neg 
の内容の正負を反転させる.

また,除算を他の演算と同じ形式で書けるように, 以下のようなマクロ命令 mdiv を定義して用いる.

mdiv eax,
eax とで演算して,結果を eax へ格納する.

その定義は以下のとおりである.

  .macro mdiv  eax, SRC
         cdq
         idiv  \SRC
  .endm

なお,上の「」「」には以下のようなものがある.(組合せによっては許されないものもある)

レジスタ
%eax, %ebx, %ecx, %edx (以上汎用レジスタ), %ebp (ベースポインタ), %esp (スタックポインタ) など.(いずれも 32bit)〈レジスタ直接〉
数値
数値を直接用いる〈イミディエイト〉
ラベル
ラベルが表わす番地のメモリ上の領域〈直接〉
[%eax*4+ラベル]
ラベルが表わす番地に %eax の内容の4倍を加えた番地のメモリ上の領域 〈インデックスの一種〉
[%ebp+数値]
ベースレジスタ %ebp の内容に数値を加えた結果を番地として,その番地のメモリ上の領域 〈ベースレジスタ相対〉
[%ebp+%eax*4+数値]
ベースレジスタ %ebp の内容に %eax の内容の4倍を加え,更に数値を加えた結果を番地として, その番地のメモリ上の領域〈ベースレジスタ相対,インデックス〉

コンパイラの概略


各ファイルの概略

src/

sample/

Q言語で書かれたプログラムのサンプル.


三浦欽也 (miura@mail.kobe-c.ac.jp), 三浦研究室