feat(plugin): Fix plugin BoxRef return and Box argument support
- Fixed deadlock in FileBox plugin copyFrom implementation (single lock) - Added TLV Handle (tag=8) parsing in calls.rs for returned BoxRefs - Improved plugin loader with config path consistency and detailed logging - Fixed loader routing for proper Handle type_id/fini_method_id resolution - Added detailed logging for TLV encoding/decoding in plugin_loader_v2 Test docs/examples/plugin_boxref_return.nyash now works correctly: - cloneSelf() returns FileBox Handle properly - copyFrom(Box) accepts plugin Box arguments - Both FileBox instances close and fini correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,266 @@
|
||||
# 🚀 Issue #001: LLVM PoC - inkwellセットアップとHello World実装
|
||||
|
||||
**タイプ**: Feature
|
||||
**優先度**: Critical
|
||||
**見積もり**: 3日
|
||||
**担当**: Copilot
|
||||
|
||||
## 📋 概要
|
||||
|
||||
Phase 9.78 LLVM PoCの第一歩として、inkwellクレートを導入し、最小限のNyashプログラム(`return 42`)をLLVM経由で実行できるようにする。
|
||||
|
||||
## 🎯 成功条件
|
||||
|
||||
以下のNyashプログラムがLLVM経由で実行され、正しい終了コードを返すこと:
|
||||
|
||||
```nyash
|
||||
// test_return_42.nyash
|
||||
static box Main {
|
||||
main() {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
期待される動作:
|
||||
```bash
|
||||
$ cargo run --features llvm -- --backend llvm test_return_42.nyash
|
||||
$ echo $?
|
||||
42
|
||||
```
|
||||
|
||||
## 📝 実装タスク
|
||||
|
||||
### 1. **Cargo.toml更新** ✅必須
|
||||
```toml
|
||||
[dependencies]
|
||||
inkwell = { version = "0.5", features = ["llvm17-0"] }
|
||||
|
||||
[features]
|
||||
llvm = ["inkwell"]
|
||||
```
|
||||
|
||||
### 2. **基本構造の作成** ✅必須
|
||||
```rust
|
||||
// src/backend/llvm/mod.rs
|
||||
pub mod context;
|
||||
pub mod compiler;
|
||||
|
||||
use crate::mir::module::MirModule;
|
||||
use crate::errors::RuntimeError;
|
||||
|
||||
pub fn compile_to_object(
|
||||
mir_module: &MirModule,
|
||||
output_path: &str,
|
||||
) -> Result<(), RuntimeError> {
|
||||
let compiler = compiler::LLVMCompiler::new()?;
|
||||
compiler.compile_module(mir_module, output_path)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **LLVMコンテキスト管理** ✅必須
|
||||
```rust
|
||||
// src/backend/llvm/context.rs
|
||||
use inkwell::context::Context;
|
||||
use inkwell::module::Module;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::targets::{Target, TargetMachine, TargetTriple, InitializationConfig};
|
||||
|
||||
pub struct CodegenContext<'ctx> {
|
||||
pub context: &'ctx Context,
|
||||
pub module: Module<'ctx>,
|
||||
pub builder: Builder<'ctx>,
|
||||
pub target_machine: TargetMachine,
|
||||
}
|
||||
|
||||
impl<'ctx> CodegenContext<'ctx> {
|
||||
pub fn new(context: &'ctx Context, module_name: &str) -> Result<Self, String> {
|
||||
// 1. ターゲット初期化
|
||||
Target::initialize_native(&InitializationConfig::default())
|
||||
.map_err(|e| format!("Failed to initialize native target: {}", e))?;
|
||||
|
||||
// 2. モジュール作成
|
||||
let module = context.create_module(module_name);
|
||||
|
||||
// 3. ターゲットマシン作成
|
||||
let triple = TargetMachine::get_default_triple();
|
||||
let target = Target::from_triple(&triple)
|
||||
.map_err(|e| format!("Failed to get target: {}", e))?;
|
||||
let target_machine = target
|
||||
.create_target_machine(
|
||||
&triple,
|
||||
"generic",
|
||||
"",
|
||||
inkwell::OptimizationLevel::None,
|
||||
inkwell::targets::RelocMode::Default,
|
||||
inkwell::targets::CodeModel::Default,
|
||||
)
|
||||
.ok_or_else(|| "Failed to create target machine".to_string())?;
|
||||
|
||||
// 4. データレイアウト設定
|
||||
module.set_triple(&triple);
|
||||
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
|
||||
|
||||
Ok(Self {
|
||||
context,
|
||||
module,
|
||||
builder: context.create_builder(),
|
||||
target_machine,
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. **最小限のコンパイラ実装** ✅必須
|
||||
```rust
|
||||
// src/backend/llvm/compiler.rs
|
||||
use inkwell::context::Context;
|
||||
use inkwell::values::IntValue;
|
||||
use crate::mir::module::MirModule;
|
||||
use crate::mir::instruction::MirInstruction;
|
||||
use super::context::CodegenContext;
|
||||
|
||||
pub struct LLVMCompiler {
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl LLVMCompiler {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
Ok(Self {
|
||||
context: Context::create(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compile_module(
|
||||
&self,
|
||||
mir_module: &MirModule,
|
||||
output_path: &str,
|
||||
) -> Result<(), String> {
|
||||
let codegen = CodegenContext::new(&self.context, "nyash_module")?;
|
||||
|
||||
// 1. main関数を探す
|
||||
let main_func = mir_module.functions.iter()
|
||||
.find(|f| f.name == "Main.main")
|
||||
.ok_or("Main.main function not found")?;
|
||||
|
||||
// 2. LLVM関数を作成
|
||||
let i32_type = codegen.context.i32_type();
|
||||
let fn_type = i32_type.fn_type(&[], false);
|
||||
let llvm_func = codegen.module.add_function("main", fn_type, None);
|
||||
|
||||
// 3. エントリブロックを作成
|
||||
let entry = codegen.context.append_basic_block(llvm_func, "entry");
|
||||
codegen.builder.position_at_end(entry);
|
||||
|
||||
// 4. MIR命令を処理(今回はReturnのみ)
|
||||
for block in &main_func.blocks {
|
||||
for inst in &block.instructions {
|
||||
match inst {
|
||||
MirInstruction::Return(Some(value_id)) => {
|
||||
// 簡易実装: 定数42を返すと仮定
|
||||
let ret_val = i32_type.const_int(42, false);
|
||||
codegen.builder.build_return(Some(&ret_val));
|
||||
}
|
||||
_ => {
|
||||
// 他の命令は今回スキップ
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 検証
|
||||
if !llvm_func.verify(true) {
|
||||
return Err("Function verification failed".to_string());
|
||||
}
|
||||
|
||||
// 6. オブジェクトファイル生成
|
||||
codegen.target_machine
|
||||
.write_to_file(&codegen.module,
|
||||
inkwell::targets::FileType::Object,
|
||||
output_path.as_ref())
|
||||
.map_err(|e| format!("Failed to write object file: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. **バックエンド統合** ✅必須
|
||||
```rust
|
||||
// src/backend/mod.rsに追加
|
||||
#[cfg(feature = "llvm")]
|
||||
pub mod llvm;
|
||||
|
||||
// src/runner.rsのrun_with_backend関数に追加
|
||||
#[cfg(feature = "llvm")]
|
||||
ExecutionBackend::LLVM => {
|
||||
// 1. オブジェクトファイル生成
|
||||
let obj_path = "nyash_output.o";
|
||||
crate::backend::llvm::compile_to_object(&mir_module, obj_path)?;
|
||||
|
||||
// 2. リンク(簡易版:システムのccを使用)
|
||||
use std::process::Command;
|
||||
let output = Command::new("cc")
|
||||
.args(&[obj_path, "-o", "nyash_output"])
|
||||
.output()
|
||||
.map_err(|e| RuntimeError::new(format!("Link failed: {}", e)))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(RuntimeError::new("Linking failed"));
|
||||
}
|
||||
|
||||
// 3. 実行
|
||||
let output = Command::new("./nyash_output")
|
||||
.output()
|
||||
.map_err(|e| RuntimeError::new(format!("Execution failed: {}", e)))?;
|
||||
|
||||
// 4. 終了コードを返す
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
Ok(Box::new(IntegerBox::new(exit_code as i64)))
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 テストケース
|
||||
|
||||
```rust
|
||||
// tests/llvm_hello_world.rs
|
||||
#[test]
|
||||
#[cfg(feature = "llvm")]
|
||||
fn test_return_42() {
|
||||
let source = r#"
|
||||
static box Main {
|
||||
main() {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
// パース → MIR生成 → LLVM実行
|
||||
let result = compile_and_run_llvm(source);
|
||||
assert_eq!(result, 42);
|
||||
}
|
||||
```
|
||||
|
||||
## 📚 参考資料
|
||||
|
||||
- [inkwell Examples](https://github.com/TheDan64/inkwell/tree/master/examples)
|
||||
- [LLVM Tutorial](https://llvm.org/docs/tutorial/)
|
||||
- [AI大会議結果](../AI-Conference-LLVM-Results.md)
|
||||
|
||||
## ⚠️ 注意事項
|
||||
|
||||
1. **LLVM依存関係**: LLVM 17がシステムにインストールされている必要があります
|
||||
2. **プラットフォーム**: まずはLinux/macOSで動作確認し、Windowsは後回し
|
||||
3. **エラーハンドリング**: 今回は最小実装のため、詳細なエラー処理は省略
|
||||
|
||||
## 🎯 次のステップ
|
||||
|
||||
このIssueが完了したら、次は:
|
||||
- Issue #002: 基本的な算術演算の実装(BinOp)
|
||||
- Issue #003: 定数値の実装(Const)
|
||||
|
||||
---
|
||||
|
||||
**作成者**: Claude + moe-charm
|
||||
**レビュアー**: AIチーム
|
||||
**関連PR**: (作成予定)
|
||||
Reference in New Issue
Block a user