Files
hakorune/docs/archive/phases/phase-15/implementation/lld-strategy.md

5.9 KiB
Raw Blame History

Phase 15 自己ホスティング実装戦略 - MIR→Cranelift→lld

Author: ChatGPT5 + Claude協議 Date: 2025-09-03 Version: 1.0

📋 概要

Nyash完全自己ホスティングを実現するための具体的実装戦略。 **「MIR→Craneliftで.o/.objを作る→lldでEXEを組む」**をNyashツールチェーンに内蔵する。

🎯 最終形(自己ホスト時の一発ボタン)

nyash build main.ny \
  --backend=cranelift \
  --target=x86_64-pc-windows-msvc   # or x86_64-unknown-linux-gnu

内部処理フロー:

  1. frontend: AST→MIR13
  2. codegen: MIR→Cranelift→.obj/.o
  3. link: lld-link(Win) / ld.lld(Linux)でEXE生成
  4. 依存ランタイムnyashrtを自動リンク(静的/動的選択)

🏗️ 実装の芯(最小で美しいやつ)

1. コード生成ライブラリC ABIファサード

// 最小限の美しいインターフェース
ny_mir_to_obj(mir_bin, target_triple) -> obj_bytes
ny_mir_jit_entry(mir_bin) -> exit_code  // 開発用
ny_free_buf(buffer)  // メモリ解放

// エラーハンドリング
// 例外は戻り値NyErrunwind禁止

実装のポイント:

  • 返却メモリはny_free_bufで解放
  • 例外は戻り値NyErrで統一unwind禁止
  • C ABIで安定した境界を作る

2. リンカー・ラッパ(プラットフォーム別)

Windows

  • 既定: lld-link
  • 主要フラグ:
lld-link <objs...> nyashrt.lib /SUBSYSTEM:CONSOLE \
  /OUT:a.exe /ENTRY:nyash_entry \
  /LIBPATH:<sdk/lib> /MACHINE:X64
  • MSVC互換が要る配布向けに/fallback:link.exeオプションも用意可

Linux

  • 既定: ld.lld(開発でmold併用可)
ld.lld -o a.out main.o -L. -lnyashrt -lc -lm -pthread \
       --gc-sections --icf=all

macOS将来

  • 日常はld64.lld、配布はXcodeのld64 + コード署名要Entitlements

3. 同梱/検出戦略

優先順: 埋め込みlld → システムlld → 代替mold/link.exe/ld64

nyash toolchain doctor  # 検出&パス設定
--linker=lld|mold|link.exe|ld64  # 明示上書き

4. ランタイム同梱

  • nyashrtを**static.a/.libshared.so/.dll**両用意
  • 既定はstatic(配布が楽)、--shared-rtで動的リンクに切替
  • WindowsはPDB生成、Linuxは-g/-Wl,--build-idでデバッグ容易に

🔧 エラー整合(ビルド失敗をわかりやすく)

エラー種別 戻り値 説明・対処
ny_mir_to_obj失敗 NYCG_ERR_* ターゲット不一致/CLIF生成失敗など
リンク失敗 リンカ標準出力 ファイル名/未解決シンボルを整形表示

診断オプション:

--emit=clif,asm,obj,link-cmd  # 診断をファイル出力(再現しやすい)

💾 キャッシュ&クロスコンパイル

オブジェクトキャッシュ

hash(MIR, target, codegen_ver).obj/.oを再利用

クロスコンパイル

--target=<triple>  # .obj/.oとリンク器/SDKを切替
  • Win用: x86_64-pc-windows-msvclld-link + MSVCライブラリ
  • Linux: x86_64-unknown-linux-gnuld.lld + glibc

Zig toolchainを併用するとクロス用のCRT/SDKが楽内部はlld

🎨 使いやすいCLI例

nyash build main.ny --backend=cranelift --release
nyash build main.ny --emit=obj,asm,clif       # 解析用
nyash run   main.ny --backend=cranelift       # JITで即実行
nyash toolchain doctor                        # lld/SDK検出

地味に効く最適化スイッチ

リンカ最適化

  • ld.lld: --gc-sections --icf=all(不要コード除去&同一関数折りたたみ)

Cranelift最適化

  • opt_level=speed
  • TypedArrayのbounds-check併合をLowerで実装

実行時最適化

  • 起動時CPUIDで関数ポインタ切替AVX2/512の専用小関数

最初の"動くまで"チェックリスト

  • ny_mir_to_objC ABI.o/.objを返せる
  • nyash link <obj> --target=<triple>lldでEXEを作れる
  • Windows/Linuxそれぞれ"Hello, Box!"実行成功
  • --emit=clif,asmでダンプが落ちる
  • 失敗時のエラーメッセージがファイル名+未解決シンボルまで出る
  • nyash toolchain doctorでlld/SDK検出

📐 実装設計詳細

LinkerBox設計

box LinkerBox {
    init { platform, linker_path, libraries, flags }
    
    link(objects, output_path) {
        local cmd = me.build_link_command(objects, output_path)
        local result = me.execute_linker(cmd)
        
        if result.exit_code != 0 {
            me.format_link_error(result.stderr)
        }
        
        return result
    }
    
    detect_linker() {
        // 優先順: 内蔵lld → システムlld → 代替
        if me.has_embedded_lld() {
            return me.extract_embedded_lld()
        }
        return me.find_system_linker()
    }
}

CraneliftBox統合

box CraneliftBox {
    init { target_triple, opt_level }
    
    compile(mir) {
        // MIR13 → Cranelift IR → Object
        local module = me.create_module()
        
        for inst in mir.instructions {
            me.lower_instruction(module, inst)
        }
        
        return module.compile()
    }
}

🌟 まとめ

  • Yes: CraneliftでEXEにするには内部でlldを叩く機能を埋め込むのが正攻法
  • 仕組みはMIR→Cranelift .o/.obj → lld
  • C ABIファサード経由でcodegenを呼び、リンカは内蔵ドライバで統一
  • これで自己ホスト→即EXE生成の"気持ちいい体験"が完成!

🔗 関連ドキュメント