Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

OpeLa 進捗報告 at 第23回自作OSもくもく会

OpeLa プロジェクトの進捗報告です。AArch64 対応とテストケースの OpeLa 化が主な話題です。VSCode によるデバッグも紹介しています。

  • Be the first to comment

  • Be the first to like this

OpeLa 進捗報告 at 第23回自作OSもくもく会

  1. 1. OpeLaの進捗 2020年12月30日 第23回自作OSもくもく会
  2. 2. OpeLaプロジェクトとは OpeLa: Operating and Language processing system OSと言語処理系を全部自作するプロジェクト OS アセンブラ コンパイラ リンカ ライブラリ 特徴:完全なセルフホスト
  3. 3. セルフホストとは(コンパイラ編) 自分自身をコンパイルすること GCCやClangはそうなっている GCCをビルドするのに他のコンパイラの助けは 不要 自作言語およびOSでこれをやりたい! 自作コンパイラ ソースコード 第1世代 自作コンパイラ 入力 第2世代 自作コンパイラ コンパイル
  4. 4. OpeLaの進捗 現在、コンパイラopelacを作成中 OpeLa言語仕様の策定&コンパイラ実装 進捗1:AArch64対応 進捗2:テスト自身をOpeLa言語で実装
  5. 5. 進捗1:AArch64対応 Mac mini (M1, 2020)を手に入れ、AArch64対応を進めた つい先日、今実装してある部分すべてがAArch64に対応した lewing@isc.tamu.edu Larry Ewing and The GIMP https://www.apple.com/jp/mac/m1/ void Push64(std::ostream& os, uint64_t v) override { os << " push " << v << 'n'; } void Push64(std::ostream& os, uint64_t v) override { os << " mov x8, " << v << 'n'; os << " str x8, [sp, #-16]!n"; } x86-64で整数をスタックにpushする命令 AArch64で整数をスタックにpushする命令
  6. 6. AArch64対応のやり方 アセンブリ命令を出力する部分を仮想クラスに抽出 各アーキ用のクラスを継承して実装 class Asm { public: enum Register { kRegL, kRegR, kRegNum, }; virtual void Push64(std::ostream& os, uint64_t v) = 0; virtual void Push64(std::ostream& os, Register reg) = 0; virtual void Pop64(std::ostream& os, Register reg) = 0; virtual void Add64(std::ostream& os, Register lhs, Register rhs) = 0; virtual void Sub64(std::ostream& os, Register lhs, Register rhs) = 0; … }; アセンブリ命令出力用の仮想クラス
  7. 7. Asmクラスを利用して命令を出力 Asm* asmgen; void GenerateAsm(ostream& os, Node* node, string_view label_break, string_view label_cont, bool lval = false) { switch (node->kind) { case Node::kInt: asmgen->Push64(os, node->value.i); return; ASTを受け取ってアセンブリを出力する関数 void Push64(std::ostream& os, uint64_t v) override { os << " push " << v << 'n'; } void Push64(std::ostream& os, uint64_t v) override { os << " mov x8, " << v << 'n'; os << " str x8, [sp, #-16]!n"; }
  8. 8. AArch64に対応して分かったこと 下手に抽象化するより、実際に複数アーキに対応する段になって 抽象化する方がうまく行く 余計な抽象化を挟まず、シンプルに保てる 必要な抽象化をもれなく導入できる AArch64には面白い命令がある 意外と複雑なアドレッシングモードがある • x86-64の「base+scale*index」に相当するアドレッシングモードがある • ldr x8, [x9, x10, lsl #3] は「x8←x9+(x10<<3)」 プレインデックス、ポストインデックス str x8, [sp, #-16]! ldr x8, [sp], #16
  9. 9. プレインデックス LDR(メモリ→レジスタ) STR(レジスタ→メモリ) で使える アドレス指定レジスタを事 前に変化させる X1にSPを指定すれば Push/Popが実現できる https://developer.arm.com/architectures/learn-the-architecture/aarch64- instruction-set-architecture/loads-and-stores-addressing
  10. 10. AArch64全般に対応したわけではない あくまでM1 Mac(Mach-O形式)のみの対応 同じAArch64でもRaspberry Piには未対応 コンパイラ・トリプルでいうところの<arch>のみの対応 将来的には、少なくともAArch64+ELFに対応させたい OpeLaの開発はLinux(ELFの世界)が主戦場だから https://clang.llvm.org/docs/CrossCompilation.html
  11. 11. 進捗2:テスト自身をOpeLa言語で実装 今まではシェルスクリプトで テストを書いていた 各テストケース毎に opelacでコンパイル →ccでリンク →バイナリを実行 を繰り返しており、遅かった テストをOpeLa言語で実装 テスト全部入りのコードを1度 だけopelacでコンパイル めちゃくちゃ高速化! test_exit 42 'func main() { 42; }' test_exit 11 'func main() { 1+23 - 13; }' test_exit 2 'func main() { 12/2 - 2 * (3 - 1); }' test_exit 5 'func main() { -3 + (+8); }' func testIntConstant() { 42; } func testAddSub() { 1+23 - 13; } func testMulDiv() { 12/2 - 2 * (3 - 1); } func testUnaryOp() { -3 + (+8); }
  12. 12. テストをOpeLa言語で書く テストケースを関数として実装 関数を呼び出し、結果を確認 https://github.com/uchan-nos/opela/blob/master/compiler/test_run.opl #define TEST_INT(want, got) testInt(want, got, #got) var (passed int; failed int;) func main() { TEST_INT(42, testIntConstant()); TEST_INT(11, testAddSub()); … TEST_INT(4 , testUserType()); print_int64(passed); print_string(" passed, "); print_int64(failed); print_string(" failedn"); return failed > 0; } func testInt(want, got int, gots *byte) { if want == got { print_string("[ OK ]: "); passed += 1; } else { print_string("[FAILED]: "); failed += 1; } print_string(gots); print_string(" -> "); print_int64(got); if want != got { print_string(", want "); print_int64(want); } print_string("n"); } テストケースを呼び出し、集計する テストケースの実行結果を確認する
  13. 13. テストケース紹介(一部) func testIntConstant() { 42; } func testAddSub() { 1+23 - 13; } func testMulDiv() { 12/2 - 2 * (3 - 1); } func testUnaryOp() { -3 + (+8); } func testRelOp() { 3 < 1; } func testRelOp2() { 2*3 >= 13/2; } func testEqOp() { 2>2 == 4<=3; } func testBlankBlock() { } func testCompoundBlock() { 42; 3; } func testDefVar() { foo:=5; bar:=3; foo*bar; } func testReturn() { return 3; 42; } func testIf() { 1; if 42 > 10 { 2; } } func testIfVar() { cond := 10 < 1; 1; if cond { 2; } } func testAssign() { foo := 3; foo = 4; foo * 2; } func testAssign2() { foo:=5; bar:=7; foo=(bar=1)=42; foo-4; } func testAssign3() { foo:=5; bar:=7; foo=bar=42; } func testFor() { i:=0; s:=0; for i <= 10 { s=s+i; i=i+1; } s; } func testAssign4() { a:=5; a=b:=3; a*b; } func testFor2() { s:=0; for i:=0; i<=10; i=i+1 { s=s+i; } } func testElse() { if 0 { 3; } else { 4; } } func testElseIf() { if 0 { 3; } else if 1 { 5; } else { 4; } } func testSimpleBlock() { 42; {} } func testNextedFor() { s:=0; for i:=1;i<3;i=i+1{ for j:=1;j<3;j=j+1{ s=s+i*j; } } s; }
  14. 14. テストをOpeLaで書いて分かったこと Cプリプロセッサが便利 特に、引数を文字列化する機能 #define TEST_INT(want, got) testInt(want, got, #got) 気づかないバグをあぶりだせた -1+5が260になるバグ • シェルスクリプトではテスト可能な値が0~255なので気づかなかった • プロセスのexit codeによる制約 ローカル変数とグローバル変数の名前衝突 • グローバル変数と同じ名前のローカル変数を定義できない 意外とOpeLaでテストが書ける!
  15. 15. 付録:VSCodeでopelacをデバッグしたい 本格的なデバッグ作業はIDEでやりたい GDBのコマンド操作より変数等が確認しやすいから VSCodeでC++のプログラムをデバッグする方法を模索した Clang + gdb + Ubuntu 18.04 lldbはVSCodeでの使用に対応してないらしい… 2つのJSONファイルの設定が重要 tasks.json: プログラムのビルド手順を設定する launch.json: プロセスの起動手順を設定する 後ほど紹介
  16. 16. VSCodeでopelacを デバッグする様子
  17. 17. tasks.json: プログラムのビルド手順を設定する { "tasks": [ { "type": "shell", "label": "C/C++: build opelac", "command": "make", "args": [ "CXX=clang++" ], "options": { "cwd": "${workspaceFolder}" }, "problemMatcher": [ "$gcc" ], … } ], "version": "2.0.0" } type=cppbuild, shell Cppbuild: Makefileを使わず、 command=clang++として直接 コンパイルする Shell: シェルコマンドなんでも使 える?(未調査) labelはlaunch.jsonから参照す る名前 problemMatcherはビルド時の ログ形式を指定
  18. 18. launch.json: プロセスの起動手順を設定する { "version": "0.2.0", "configurations": [ { "name": "opelacのビルドとデバッグ", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/opelac", "args": ["<${workspaceFolder}/hgoe.opl"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": […], "preLaunchTask": "C/C++: build opelac", "miDebuggerPath": "/usr/bin/gdb" } ] } programは起動対象 標準入力にファイルを渡す にはargsに"<file" preLaunchTaskにビルドタ スクを指定 miDebuggerPath MI=Machine Interface lldbはMIに対応してないぽい のでgdbを使おう
  19. 19. まとめ OpeLaプロジェクトの概要 AArch64対応 テストのOpeLa化 VSCodeでのデバッグ
  20. 20. 自作言語をセルフホストする道のり 第1世代 自作コンパイラ opelac.cpp clang++ 入力 第1世代 自作コンパイラ コンパイル 第2世代 自作コンパイラ opelac.opl 書き直し コンパイル 入力 第2世代 自作コンパイラ コンパイル 第3世代 自作コンパイラ 入力 ここまでくれば ぐるぐる回せる (はず)
  21. 21. OpeLaで目指す「完全セルフホスト」 自作OSの上で動く自作言語処理系で 自作OSと自作言語処理系をビルドすること 自作OS 自作言語処理系 自作OS ソースコード 自作言語処理系 ソースコード Linux GCC,binutils LinuxとGCCの ソースコード Linuxでいえば

    Be the first to comment

OpeLa プロジェクトの進捗報告です。AArch64 対応とテストケースの OpeLa 化が主な話題です。VSCode によるデバッグも紹介しています。

Views

Total views

124

On Slideshare

0

From embeds

0

Number of embeds

0

Actions

Downloads

0

Shares

0

Comments

0

Likes

0

×