noniccは、低レイヤを知りたい人のためのCコンパイラ作成入門に基づいた、Rustで書かれたコンパイラです。
C言語のサブセットをコンパイルし、x86-64アーキテクチャ向けのLinuxで実行可能なアセンブリコードを生成します。
- アーキテクチャ: x86-64
- OS: Linux (SysV ABI, ELF)
- CPU: x86-64 (AMD Ryzen 5 PRO 8640HS)
- OS: Ubuntu 24.04.3 LTS (WSL2)
- Toolchain:
- rustc 1.89.0 (2025-08-04)
- GNU Binutils 2.42
以下の文法をサポートしています。
- 型:
int型変数宣言 (int x;)- ポインタ型 (
*,**, ...)、アドレス演算子 (&)、間接参照演算子 (*)
- 制御構文:
- 条件分岐 (
if-else) - ループ (
for,while)
- 条件分岐 (
- 関数:
- 関数の定義と呼び出し
- 最大6個までの引数
- 演算子:
- 四則演算 (
+,-,*,/) - 比較演算子 (
==,!=,<,<=,>,>=) - 代入 (
=) - 単項演算子 (
&,*)
- 四則演算 (
- その他:
- ブロック (
{ ... }) return文
- ブロック (
noniccは、以下の流れでコンパイルを行います。
- 字句解析 (Lexer/Tokenizer): 入力されたソースコードをトークン列に変換します。
- 構文解析 (Parser): トークン列を元に、抽象構文木(AST)を構築します。
- 中間表現 (IR) 生成: ASTを、三番地コード(Three-address code)ベースの中間表現に変換します。
- レジスタ割り当て: 仮想レジスタを持つIRに対し、線形スキャン法を用いて物理レジスタ(x86-64)を割り当てます。
- コード生成 (Code Generation): レジスタが割り当てられたIRを元に、最終的なx86-64アセンブリコード(Intel記法)を生成します。
現時点では、以下の制約があります。
- レジスタのスピル未実装: レジスタが足りなくなった場合に、スタックへ退避させるロジック(スピル)が実装されていません。そのため、長いコードや複雑な式ではレジスタが枯渇し、コンパイルできない場合があります。
- 引数の数: 関数の引数は6個までに制限されています。
- 型チェックの欠如: int型とポインタ型の区別など、静的な型検証は行われません。
- 未サポートの機能:
- break, continue 文
- グローバル変数
- char型や配列、構造体などの複合型
- for文の初期化式における変数宣言 (
for (int i = 0; ...)
git clone https://github.com/no2ca/nonicc.git
cd nonicc
cargo build --releaseコンパイラへの入力として、ソースコードを文字列で渡します。
# 以下は "42" を返すプログラムをコンパイルし、実行する例
./target/release/nonicc "int main() { int x; x=42; return x; }" > tmp.s
gcc -o tmp tmp.s
./tmp
echo $?
# 42このコードは 1+2+...+10 = 55 を計算し、終了コード 55 を返します。
int main() {
int sum;
sum = 0;
int i;
for (i = 1; i <= 10; i = i + 1) {
sum = sum + i;
}
return sum;
}このコードは gcd(42, 30) を計算し、終了コード 6 を返します。
int gcd(int a, int b) {
while (a != b) {
if (a > b) {
a = a - b;
} else {
b = b - a;
}
}
return a;
}
int main() {
return gcd(42, 30);
}このコードは実行すると終了コード 3 を返します。
int main() {
int a;
int *p;
int **pp;
int ***ppp;
a = 1;
p = &a;
pp = &p;
ppp = &pp;
***ppp = 3;
return a;
}このコードは、ack(3, 4) を計算し、終了コード 125 を返します。
int ack(int m, int n) {
if (m == 0) return n + 1;
if (n == 0) return ack(m - 1, 1);
return ack(m - 1, ack(m, n - 1));
}
int main() {
return ack(3, 4);
}