# nlux

- nlcc		フルスクラッチで開発された，簡単なＣコンパイラ
- nlsh		フルスクラッチで開発された，簡単なシェル
- nllibc	フルスクラッチで開発された，簡単な標準Ｃライブラリ
- nlline	フルスクラッチで開発された，簡単なreadline互換ライブラリ
- nll		フルスクラッチで開発された，簡単なスクリプト言語
- nltl		フルスクラッチで開発された，簡単なコンパイラ型言語
- nlux		それらの集合体の総称

No Look, No Listen, No Learn, No Library
何も見ず，何も参考にせずに，フルスクラッチで開発しています． 
(理由は，そうしたいからです)

すべてをnlccでコンパイルすることができます．
すべてをnllibcやnllineをリンクしてビルドすることができます．

つまりnlccやnlshを，nllibcやnllineをリンクしてビルドすることで，
システムに付属のライブラリを一切使わずに閉じてビルドすることができます．
またそれらをnlccでビルドすることができます．

なので，例えばnlccをビルドして，そのnlccでnllibcをビルドして，そのnllibcを
利用してnlccをnlccでビルドして，．．．といったことができます．

基本としてFreeBSD／Debian環境で動作します．

# nlccの特徴

セルフビルドができます．(自分自身で自分自身をコンパイルできます)
gccでnlccをビルドして，そのnlccでまたnlccをビルドして，そのnlccでまたnlccを
ビルドして，それらの実行ファイルが一致することを確認してあります．
つまりgccでビルドしたnlccと，nlccでビルドしたnlccの動作が一致する，ということ
です．nlccはセルフビルド向けに書いているということはなく，作者が普段どおりの
コーディングスタイルで書いていますので，作者が普段書くようなプログラムは
コンパイルできる，ということでもあります．

各種アーキテクチャへの対応(クロスコンパイラの作成)が非常に低いコストで
できます．asm_code_*.c というファイルを作成することでアーキテクチャ対応が
できます(６百行程度のファイルを作成するだけで，新たなアーキテクチャに対応
できるということです)．実際に様々なアーキテクチャにサンプル対応してあります．

- x86/amd64	セルフビルドや各種テストが通ることを確認
- ARM/AArch64/Thumb/Thumb2/MIPS/MIPS16/PowerPC/RISC-V/RX
		cross-gcc11の環境で動作を確認
- OSECPU	実験的対応

ABIはgccに合わせているので，gccでビルドしたオブジェクトファイルとnlccで
ビルドしたオブジェクトファイルをリンクして動作させることができます．

高度な最適化をしません．このため生成される実行コードは非常に無駄が多く速度も
速くはありません．多種アーキテクチャにいかに楽に対応できるか，というところを
重要視して開発しています．このため動作や生成コードを可能な限りアーキテクチャ間
で共通化しており，そのため無駄が多くなっています．でもアーキテクチャ対応は簡単
にできます．(基本とするレジスタを２つ選んで，各種命令を登録するだけです)

アーキテクチャ対応の簡単さは，ビルトインで実現している部分が多くあります．
例えばかけ算や割算は，それらのための命令を登録しなくても構いません．
その場合，かけ算をソフトウェアで行うビルトインが組み込まれます．ループを回して
かけ算を行うため非常に低速ですが，それでもかけ算命令を利用せずに実行ファイルを
生成できます．シフト演算や符号拡張なども，そのための命令を登録しなくても
ビルトインで実現可能です．

可変長引数，ビットフィールド，構造体の引数渡しにも対応しています．
構造体の返り値には，不十分ですが対応しています．
(返すことはできるがスタックの解放済み部分を利用してしまう)

64ビット変数(long longなど)は，64ビット環境でビルドした場合には64ビットとして
動作します．32ビット環境でビルドした場合には，64ビット変数は使えますし
変数サイズも64ビットで確保されますが，内部の演算等は32ビットで行われます．
(64ビット演算のエミュレーションはしていない，ということです)

- nlcc1		Ｃコンパイラ

# nllibcの特徴

標準Ｃライブラリです．nlccやnlshをビルドするための最低限＋αくらいしか実装
されていません．
システムコール・ラッパーやスタートアップ(いわゆるcrt)やリンカスクリプトも含んで
います．このためシステムの標準Ｃライブラリを一切使わずに，完全に閉じてビルド
することが可能です．

各種アーキテクチャに対応しています．以下を参照してください．

	% ls nllibc/crt
	% ls nllibc/lib/arch

ビルドすると，以下の３種類のライブラリが生成されます．

- nlcrt.o	スタートアップ
- libnlc.a	標準Ｃライブラリ
- libnlterm.a	簡単なtermios(nllineで入力制御を行うため)

# nlshの特徴

極小のシェルです．tcshに似ています．
プロセス管理(バックグラウンド実行やサスペンド等)ができます．
パイプやリダイレクションには対応しています．
シェルスクリプトには対応していません．

行編集にreadlineライブラリを利用します．editlineやlibeditを利用することも
できます．それらが利用できない場合にはgetline()やfgets()によって代替することも
できます．(ただし行編集はできなくなります)
nllineを用いることでreadline不要でビルドできます．(この場合は行編集が可能です)
またnllibcも用いることで，外部ライブラリを用いず完全に閉じてビルドが可能です．

# 使いかた

Makefileを用意しています．
まずトップにあるMakefileの，以下を環境に合わせて修正してください．

	BUILD = freebsd
	ARCH = x86_64

以下は利用例です．

	% make build.nlcc1	nlccをビルド
	% make install.nlcc1	nlccをビルドしインストール
	% make install.nllibc	nllibcをビルドしインストール
	% make clean		ビルドされたモジュールを削除
	% make uninstall	インストールされたモジュールをアンインストール

インストールはnluxのトップディレクトリにされます．

	% make build.nlcc1.nlcc1
			インストールされたnlccを用いて，nlccをビルドします
	% make install.nlcc1.nllibc
			インストールされたnllibcを用いて，nlccをビルドし
			インストールします
	% make install.nlcc1.nllibcall
			インストールされたnllibcを用いて，nlccをビルドし
			インストールします
			(crtやリンカスクリプトもnllibcのものを利用します)
	% make install.nlcc1.nlcc1_nllibcall
			インストールされたnlccとnllibcを用いて，nlccをビルドし
			インストールします
	% make install.nllibc.nlcc1
			インストールされたnlccを用いて，nllibcをビルドし
			インストールします
	% make install.nllibc.nlcc1_nllibc
			インストールされたnlccを用いて，nllibcをビルドし
			インストールします
			(ヘッダファイルもnllibcのものを利用します)

これらを組み合わせることで，まずnlccをインストールして，そのnlccを用いてnllibc
をビルドして，そのnllibcを用いてnlccをビルドして．．．のようなことができます．
(例えば以下の順番でビルドします)

	% make install.nlcc1
	% make install.nllibc.nlcc1_nllibc
	% make install.nlcc1.nllibcall

nlccを用いてnlccをビルドしたい場合には，以下のようにします．

	% make install.nlcc1
	% make clean.nlcc1
	% make install.nlcc1.nlcc1

makeのすべてのターゲットはmakeの-jオプションに対応しています．
マルチコア環境では以下のようにすることで，高速にビルドできます．

	% make -j 4 build.nlcc1

以下はnlcc/nllibc/nlline等を利用して，一通りを生成する例です．
(上から順に実行します)

	% make clean ; make uninstall
	% make install.nlcc1
			nlccを生成
	% make clean.nlcc1 ; make install.nlcc1.nlcc1
			生成したnlccでnlccを再生成
	% make install.nllibc.nlcc1_nllibc
			生成したnlccでnllibc/nltermを生成
	% make clean.nlcc1 ; make install.nlcc1.nlcc1_nllibcall
			生成したnlcc/nllibcでnlccを再生成
	% make install.nlline.nlcc1_nllibc_nlterm
			生成したnlcc/nllibc/nltermでnllineを生成
	% make install.nll.nlcc1_nlline_nlterm_nllibcall
			生成したnlcc/nllibc/nlterm/nllineでnllを生成
	% make test.nlcc1
	% make test.nll
	% make test.nllibc
			生成したnlcc/nllibc/nllをテスト
	% ./bin/nll
	nll>
			生成したnllを起動

# テスト

テストを自動化しています．
nlccのテストは，テスト用のプログラムをgcc等でビルドした場合とnlccでビルドした
場合の実行ファイルの実行結果が一致することを確認しています．
nllibcのテストは，テスト用のプログラムをシステムの標準Ｃライブラリをリンクして
動作させた場合とnllibcをリンクして動作させた場合で，実行結果が一致することを
確認してあります．
このように，nlcc/nllibcに対して，gccやシステムの標準Ｃライブラリを利用した場合
の動作と比較して一致性を確認することで，テストプログラムの作成コストを大幅に
下げています．

トップディレクトリで以下のようにして，テストが行えます．

	% make test.nlcc1	インストールされたnlccの動作のテストをする
	% make test.nllibc	インストールされたnllibcの動作のテストをする

例えば以下のようにすれば，nlccでビルドしたnlccの動作のテストができます．

	% make install.nlcc1
	% make install.nlcc1.nlcc1
	% make test.nlcc1

以下のようにすれば，nlccでビルドしたnllibcの動作のテストができます．

	% make install.nlcc1
	% make install.nllibc.nlcc1
	% make test.nllibc

テスト用のターゲットも，makeの-jオプションに対応しています．
マルチコア環境では以下のようにすることで，高速にテストができます．

	% make -j 4 test.nlcc1

テスト時にはtestsuite以下にワーキングファイルが生成されますが，以下の
TESTOBJDIR を修正することで，/tmpを利用することができます．/tmpをメモリファイル
システムにしている場合には，テストが高速化できます．

	testsuite/nlcc1/Makefile
	testsuite/nllibc/Makefile

	-TESTOBJDIR = testobj
	-#TESTOBJDIR = /tmp/testobj_nlcc1
	+#TESTOBJDIR = testobj
	+TESTOBJDIR = /tmp/testobj_nlcc1

以下でテスト結果を再生成します．サンプルプログラムをgccやシステムの
標準Ｃライブラリを利用してテスト結果を生成し，その後にテストを行った場合の
比較元を生成します．(新たにテストを作成した場合に行います)

	% cd testsuite/nlcc1
	% make testset

# クロスコンパイル

クロスコンパイルに対応しています．
(ただしアセンブラ・リンカが必要なため，以下のcross-gcc494もしくはcross-gcc11
の環境を利用します)

	https://kozos.jp/books/asm/

以下はnllをAndroid(ARM)向けにクロスコンパイルする例です．

	% make USE_FLOATING_POINT=yes install.nll-android-arm

以下はMinGWを用いてnllをWindows向けにクロスコンパイルする例です．
(SDLが必要です．SDLを用いない場合は USE_SDL2= を指定してください)
※ SDL未使用でも，(ウィンドウ画面は出ませんが)グラフィック機能は使えます

	% make install.nll-mingw

以下はnlccでクロスコンパイルしたり，nllibcをクロスコンパイルする例です．

(cross-gcc11を用いてnllibcをARM向けにビルド)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=arm-eabi CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc install.nllibc

(nlccを用いてnllibcをARM向けにビルド)
※ ただしアセンブラ等のためにcross-gcc11も利用
※ Linuxの場合はOSDEFを__linux__にする．(システムコール・ラッパー等の選択のため)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=arm-eabi CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc \
		CC1="$NLROOTDIR/bin/nlcc1 -march=arm" \
		install.nllibc

(nlccを用いてnllibcをRISC-V向けにビルド)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=riscv-elf CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc \
		CC1="$NLROOTDIR/bin/nlcc1 -march=riscv32" \
		install.nllibc

(nlccを用いてnllibcをThumb向けにビルド)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=arm-eabi CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc \
		CC1="$NLROOTDIR/bin/nlcc1 -march=thumb" \
		AOPTFLAGS="-mthumb" LOPTFLAGS="-mthumb" install.nllibc

(nlccを用いてnllibcをThumb2向けにビルド)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=arm-eabi CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc \
		CC1="$NLROOTDIR/bin/nlcc1 -march=thumb2" \
		AOPTFLAGS="-march=armv7-a -mthumb" LOPTFLAGS="-march=armv7-a -mthumb" \
		install.nllibc

(nlccを用いてnllibcをMIPS16向けにビルド)

	% make CCDIR=/usr/local/cross-gcc11 CROSS=mips-elf CCOMPILER=gcc \
		OSDEF=__FreeBSD__ STDCHEADERS=nllibc \
		CC1="$NLROOTDIR/bin/nlcc1 -march=mips16" \
		AOPTFLAGS="-mips16" LOPTFLAGS="-mips16" install.nllibc

(cross-gcc11/execの環境で，nlccを用いてサンプルプログラムをARM向けにビルドし実行)

	% make CROSS_CC1="$NLROOTDIR/bin/nlcc1 -march=arm" \
		arm-eabi.sot

(cross2-gcc11/printfの環境で，nlccとnllibcを用いてサンプルプログラムをビルドし実行)
※ Linuxの場合はPOPTFLAGSで__linux__を指定

	% make CROSS_CC1="$NLROOTDIR/bin/nlcc1 -march=arm" \
		INCDIR="$NLROOTDIR/include" LIBDIR="$NLROOTDIR/lib" \
		POPTFLAGS="-nostdinc -D__FreeBSD__" LIBS="-lnlc -lgcc" arm-eabi.sot

# nlccでの新アーキテクチャ対応

新アーキテクチャに対応する場合，asm_code_(アーキテクチャ名).[ch] というファイル
を作成し，コンパイル対象にしてください．ファイルの作成方法は，既存のファイルを
参考にしてください．また asm_code.h にアーキテクチャを登録し，main.cに
オプションを追加してください．

nlccでは演算などのためにレジスタを２つ使います．これをR0/R1と呼びます．
R0/R1にするレジスタを選んでください．基本的には以下のように選びます．

- R0	関数の戻り値を渡すレジスタ
- R1	R0との演算が行えるレジスタ

例として，x86ではR0にEAX，R1にEDXを利用しています．
それらのレジスタを利用して演算などを行う関数を asm_code_XXX.c に作成して
ください．asm_code_i386.c あたりを参考にしてください．

nlccは主にR0を利用して値のやりとりをするようなコードを生成します．また補助的に
R1を利用します．メインで利用するレジスタを２つに限定することで，コード効率は
悪いのですがアーキテクチャ対応をシンプルなものにしています．

一時的な保存場所として利用するレジスタを，tmp_regs[]に登録しておきます．これら
のレジスタは値が上書きされるため，基本としてcallee-savedのレジスタ
(asm_code_XXXX_function_register_save()によって保存されるレジスタ)を登録して
ください．ただしよくわからなければ，無しでも構いません．
(その場合，保存場所としてスタックが利用されます)

以下の関数は命令を登録しなくても，-1を返すことでビルトインが利用されるように
なります．他の命令を組み合わせたりすることで，ソフトウェア的にエミュレーション
します．ただし動作は遅くなります．(例えばかけ算はループを回して加算を行うことで
計算するため，非常に低速になります)

|命令		|ビルトインの強制利用のためのオプション	|
|---------------|---------------------------------------|
|符号拡張	|USE_BUILTIN_EXTENSION			|
|ビット反転	|USE_BUILTIN_INV			|
|符号反転	|USE_BUILTIN_MINUS			|
|排他的論理和	|USE_BUILTIN_XOR			|
|かけ算		|USE_BUILTIN_MUL			|
|除算		|USE_BUILTIN_DIV			|
|剰余算		|USE_BUILTIN_MOD			|
|左シフト	|USE_BUILTIN_LSHIFT			|
|右シフト	|USE_BUILTIN_RSHIFT			|
|分岐(一部)	|USE_BUILTIN_BRANCH			|
|比較(一部)	|USE_BUILTIN_CMP			|
|関数呼び出し	|USE_BUILTIN_CALL			|
|R1に対する処理	|USE_BUILTIN_R1				|
|---------------|---------------------------------------|
|上記すべて	|USE_BUILTIN				|

どの関数がビルトイン利用可能かについては，asm_code.c 内で上記のオプションに
よってビルトインに切り替えている箇所を参照してください．ビルトインへの切替えを
行えるようになっている関数は，基本的に-1を返すことでビルトイン利用が可能です．

逆に，ビルトインを利用することで，命令は未登録でもひとまず(遅くてもいいので)
動作させるということができます．新規アーキテクチャ対応時には，ひとまず
ビルトイン利用とすることで初期の対応コストを低くして，動作しはじめたら徐々に
命令を登録していく，というように段階を踏んで実装していくことができます．

ここまで
