archive: Move JIT/Cranelift to archive during Phase 15 focus
Phase 15 requires concentrated development on PyVM and LLVM backends only. JIT/Cranelift was causing build confusion and distracting AI developers. ## Archived Components - src/jit/ → archive/jit-cranelift/src/jit/ - src/backend/cranelift/ → archive/jit-cranelift/src/backend/cranelift/ - JIT Box modules → archive/jit-cranelift/src/boxes/ - JIT scripts → archive/jit-cranelift/scripts/, tools/ - clif_adapter.rs → archive/jit-cranelift/src/semantics/ ## Build Changes - Cargo.toml: Comment out cranelift-jit feature and dependencies - src/lib.rs: Disable JIT module declaration - src/boxes/mod.rs: Disable JIT Box module declarations - src/semantics/mod.rs: Disable clif_adapter module - debug_box.rs: Replace JIT calls with archive stubs ## Documentation - archive/jit-cranelift/ARCHIVE_NOTES.md: Complete restoration guide - Reason: Phase 15 selfhosting focus (80k→20k line reduction) - Restoration: Full procedure documented for future revival This eliminates build errors and AI developer confusion, enabling focused Phase 15 development on PyVM/LLVM backends only. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
96
archive/jit-cranelift/ARCHIVE_NOTES.md
Normal file
96
archive/jit-cranelift/ARCHIVE_NOTES.md
Normal file
@ -0,0 +1,96 @@
|
||||
# JIT/Cranelift アーカイブ - Phase 15
|
||||
|
||||
## 📅 アーカイブ日時
|
||||
2025-09-23 - Phase 15 セルフホスティング集中開発期間中
|
||||
|
||||
## 🎯 アーカイブ理由
|
||||
|
||||
### 現在の開発焦点
|
||||
- **Phase 15**: Nyashセルフホスティング(80k→20k行革命)
|
||||
- **開発対象**: PyVMとLLVMバックエンドのみ
|
||||
- **JIT/Cranelift**: 現在未使用、ビルドエラーや混乱の原因
|
||||
|
||||
### 具体的な問題
|
||||
1. **ビルドエラー**: `cranelift-jit` フィーチャーでのビルド失敗
|
||||
2. **AI開発者の混乱**: JSON開発Claude Codeが誤ってJITルートを参照
|
||||
3. **リソース分散**: メンテナンスコストが高い
|
||||
4. **Phase 15集中**: PyVM/LLVM開発に集中したい
|
||||
|
||||
## 📂 アーカイブ内容
|
||||
|
||||
### 移動されたディレクトリ・ファイル
|
||||
```
|
||||
archive/jit-cranelift/
|
||||
├── src/
|
||||
│ ├── jit/ # JIT実装コア(ABI、エンジン、ローワリング等)
|
||||
│ └── backend/cranelift/ # Craneliftバックエンド実装
|
||||
├── scripts/
|
||||
│ └── build_jit.sh # JITビルドスクリプト
|
||||
└── tools/
|
||||
├── jit_compare_smoke.sh # JIT比較スモークテスト
|
||||
└── smokes/
|
||||
├── jit_smoke.sh # JITスモークテスト
|
||||
└── smoke_vm_jit.sh # VM-JIT比較テスト
|
||||
```
|
||||
|
||||
### Cargo.toml変更
|
||||
- `cranelift-jit` フィーチャーをコメントアウト
|
||||
- JIT関連の依存関係を無効化
|
||||
|
||||
## 🔄 復活手順(将来用)
|
||||
|
||||
### 1. ファイル復元
|
||||
```bash
|
||||
# アーカイブから復元
|
||||
mv archive/jit-cranelift/src/jit src/
|
||||
mv archive/jit-cranelift/src/backend/cranelift src/backend/
|
||||
mv archive/jit-cranelift/scripts/build_jit.sh .
|
||||
mv archive/jit-cranelift/tools/* tools/
|
||||
```
|
||||
|
||||
### 2. Cargo.toml復元
|
||||
```toml
|
||||
[features]
|
||||
cranelift-jit = ["dep:cranelift", "dep:cranelift-jit", "dep:cranelift-module"]
|
||||
|
||||
[dependencies]
|
||||
cranelift = { version = "0.103", optional = true }
|
||||
cranelift-jit = { version = "0.103", optional = true }
|
||||
cranelift-module = { version = "0.103", optional = true }
|
||||
```
|
||||
|
||||
### 3. ビルド確認
|
||||
```bash
|
||||
# JITビルドテスト
|
||||
cargo build --release --features cranelift-jit
|
||||
|
||||
# スモークテスト実行
|
||||
./tools/jit_smoke.sh
|
||||
```
|
||||
|
||||
### 4. 統合テスト
|
||||
- PyVM vs JIT性能比較
|
||||
- LLVM vs JIT出力比較
|
||||
- 全バックエンド統合テスト
|
||||
|
||||
## 💡 設計ノート
|
||||
|
||||
### JIT実装の特徴
|
||||
- **Cranelift統合**: Wasmtime/Craneliftエコシステム活用
|
||||
- **ホストコール最適化**: Rustネイティブ関数との高速ブリッジ
|
||||
- **メモリ管理**: GCとJITの協調動作
|
||||
- **デバッグ支援**: JIT統計・トレース機能
|
||||
|
||||
### 将来的な価値
|
||||
- **高速実行**: 本格運用時の性能向上
|
||||
- **AOTコンパイル**: ネイティブバイナリ生成
|
||||
- **WebAssembly統合**: WASM実行環境との統一
|
||||
|
||||
## 📋 関連Issue・PR
|
||||
- JIT/Craneliftビルドエラー修正が必要
|
||||
- AIエージェント向けドキュメント整備
|
||||
- Phase 15完了後の復活検討
|
||||
|
||||
---
|
||||
|
||||
**Note**: このアーカイブは一時的な措置です。Phase 15完了後、JIT/Craneliftの復活と最新化を検討します。
|
||||
5
archive/jit-cranelift/scripts/build_jit.sh
Normal file
5
archive/jit-cranelift/scripts/build_jit.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# JIT (Cranelift) ビルド - 24スレッド並列
|
||||
echo "🚀 JIT (Cranelift) ビルドを開始します..."
|
||||
cargo build --release --features cranelift-jit -j 24
|
||||
echo "✅ JIT ビルド完了!"
|
||||
201
archive/jit-cranelift/src/backend/cranelift/builder.rs
Normal file
201
archive/jit-cranelift/src/backend/cranelift/builder.rs
Normal file
@ -0,0 +1,201 @@
|
||||
/*!
|
||||
* ClifBuilder - IRBuilder implementation for Cranelift (skeleton)
|
||||
*
|
||||
* This satisfies the IRBuilder trait so LowerCore can target it.
|
||||
* Actual CLIF emission will be added incrementally.
|
||||
*/
|
||||
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
use crate::jit::lower::builder::{BinOpKind, CmpKind, IRBuilder, ParamKind};
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
|
||||
// Minimal recorded opcodes for Const/Add/Return first
|
||||
enum RecOp {
|
||||
ConstI64(i64),
|
||||
ConstF64(f64),
|
||||
BinOp(BinOpKind),
|
||||
Return,
|
||||
}
|
||||
|
||||
pub struct ClifBuilder {
|
||||
pub consts: usize,
|
||||
pub binops: usize,
|
||||
pub cmps: usize,
|
||||
pub branches: usize,
|
||||
pub rets: usize,
|
||||
ops: Vec<RecOp>,
|
||||
}
|
||||
|
||||
impl ClifBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
consts: 0,
|
||||
binops: 0,
|
||||
cmps: 0,
|
||||
branches: 0,
|
||||
rets: 0,
|
||||
ops: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build and execute the recorded ops as a native function using Cranelift
|
||||
pub fn finish_and_execute(&self) -> Result<i64, String> {
|
||||
use cranelift_codegen::ir::{types, AbiParam, Signature};
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_module::{Linkage, Module};
|
||||
// JIT setup
|
||||
let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
|
||||
let flag_builder = cranelift_codegen::settings::builder();
|
||||
let flags = cranelift_codegen::settings::Flags::new(flag_builder);
|
||||
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
|
||||
let jit_builder =
|
||||
cranelift_jit::JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||
let mut module = cranelift_jit::JITModule::new(jit_builder);
|
||||
// Signature ()->i64
|
||||
let mut sig = Signature::new(module.target_config().default_call_conv);
|
||||
sig.returns.push(AbiParam::new(types::I64));
|
||||
let func_id = module
|
||||
.declare_function("ny_lowercore_main", Linkage::Export, &sig)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut ctx = module.make_context();
|
||||
ctx.func.signature = sig;
|
||||
let mut fbc = FunctionBuilderContext::new();
|
||||
let mut fb = FunctionBuilder::new(&mut ctx.func, &mut fbc);
|
||||
let entry = fb.create_block();
|
||||
fb.switch_to_block(entry);
|
||||
|
||||
// Interpret ops with a small value stack of CLIF Values
|
||||
let mut vs: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
let mut did_return = false;
|
||||
for op in &self.ops {
|
||||
match *op {
|
||||
RecOp::ConstI64(i) => {
|
||||
vs.push(fb.ins().iconst(types::I64, i));
|
||||
}
|
||||
RecOp::ConstF64(f) => {
|
||||
let fv = fb.ins().f64const(f);
|
||||
let iv = fb.ins().fcvt_to_sint(types::I64, fv);
|
||||
vs.push(iv);
|
||||
}
|
||||
RecOp::BinOp(BinOpKind::Add) => {
|
||||
if vs.len() < 2 {
|
||||
vs.clear();
|
||||
vs.push(fb.ins().iconst(types::I64, 0));
|
||||
} else {
|
||||
let r = vs.pop().unwrap();
|
||||
let l = vs.pop().unwrap();
|
||||
vs.push(fb.ins().iadd(l, r));
|
||||
}
|
||||
}
|
||||
RecOp::BinOp(_) => { /* ignore others for now */ }
|
||||
RecOp::Return => {
|
||||
let retv = if let Some(v) = vs.last().copied() {
|
||||
v
|
||||
} else {
|
||||
fb.ins().iconst(types::I64, 0)
|
||||
};
|
||||
fb.ins().return_(&[retv]);
|
||||
did_return = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure function ends with return
|
||||
if !did_return {
|
||||
let retv = if let Some(v) = vs.last().copied() {
|
||||
v
|
||||
} else {
|
||||
fb.ins().iconst(types::I64, 0)
|
||||
};
|
||||
fb.ins().return_(&[retv]);
|
||||
}
|
||||
fb.seal_block(entry);
|
||||
fb.finalize();
|
||||
module
|
||||
.define_function(func_id, &mut ctx)
|
||||
.map_err(|e| e.to_string())?;
|
||||
module.clear_context(&mut ctx);
|
||||
let _ = module.finalize_definitions();
|
||||
let code = module.get_finalized_function(func_id);
|
||||
let func = unsafe { std::mem::transmute::<_, extern "C" fn() -> i64>(code) };
|
||||
Ok(func())
|
||||
}
|
||||
}
|
||||
|
||||
impl IRBuilder for ClifBuilder {
|
||||
fn begin_function(&mut self, _name: &str) {}
|
||||
fn end_function(&mut self) {}
|
||||
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {}
|
||||
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {}
|
||||
fn emit_param_i64(&mut self, _index: usize) {}
|
||||
fn emit_const_i64(&mut self, val: i64) {
|
||||
self.consts += 1;
|
||||
self.ops.push(RecOp::ConstI64(val));
|
||||
}
|
||||
fn emit_const_f64(&mut self, val: f64) {
|
||||
self.consts += 1;
|
||||
self.ops.push(RecOp::ConstF64(val));
|
||||
}
|
||||
fn emit_binop(&mut self, op: BinOpKind) {
|
||||
self.binops += 1;
|
||||
self.ops.push(RecOp::BinOp(op));
|
||||
}
|
||||
fn emit_compare(&mut self, _op: CmpKind) {
|
||||
self.cmps += 1;
|
||||
}
|
||||
fn emit_jump(&mut self) {}
|
||||
fn emit_branch(&mut self) {
|
||||
self.branches += 1;
|
||||
}
|
||||
fn emit_return(&mut self) {
|
||||
self.rets += 1;
|
||||
self.ops.push(RecOp::Return);
|
||||
}
|
||||
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) {}
|
||||
fn emit_host_call_typed(
|
||||
&mut self,
|
||||
_symbol: &str,
|
||||
_params: &[ParamKind],
|
||||
_has_ret: bool,
|
||||
_ret_is_f64: bool,
|
||||
) {
|
||||
}
|
||||
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) {
|
||||
}
|
||||
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) {}
|
||||
fn prepare_blocks(&mut self, _count: usize) {}
|
||||
fn switch_to_block(&mut self, _index: usize) {}
|
||||
fn seal_block(&mut self, _index: usize) {}
|
||||
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) {}
|
||||
fn jump_to(&mut self, _target_index: usize) {}
|
||||
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) {}
|
||||
fn ensure_block_params_b1(&mut self, index: usize, count: usize) {
|
||||
self.ensure_block_params_i64(index, count);
|
||||
}
|
||||
fn ensure_block_param_i64(&mut self, index: usize) {
|
||||
self.ensure_block_params_i64(index, 1);
|
||||
}
|
||||
fn push_block_param_i64_at(&mut self, _pos: usize) {}
|
||||
fn push_block_param_b1_at(&mut self, pos: usize) {
|
||||
self.push_block_param_i64_at(pos);
|
||||
}
|
||||
fn push_block_param_i64(&mut self) {
|
||||
self.push_block_param_i64_at(0);
|
||||
}
|
||||
fn br_if_with_args(
|
||||
&mut self,
|
||||
_then_index: usize,
|
||||
_else_index: usize,
|
||||
_then_n: usize,
|
||||
_else_n: usize,
|
||||
) {
|
||||
self.emit_branch();
|
||||
}
|
||||
fn jump_with_args(&mut self, _target_index: usize, _n: usize) {
|
||||
self.emit_jump();
|
||||
}
|
||||
fn hint_ret_bool(&mut self, _is_b1: bool) {}
|
||||
fn ensure_local_i64(&mut self, _index: usize) {}
|
||||
fn store_local_i64(&mut self, _index: usize) {}
|
||||
fn load_local_i64(&mut self, _index: usize) {}
|
||||
}
|
||||
114
archive/jit-cranelift/src/backend/cranelift/context.rs
Normal file
114
archive/jit-cranelift/src/backend/cranelift/context.rs
Normal file
@ -0,0 +1,114 @@
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use cranelift_codegen::ir::{types, AbiParam, Block, Signature};
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
use cranelift_module::{FuncId, Linkage, Module};
|
||||
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
|
||||
/// Simple block map (MIR BB -> CLIF Block)
|
||||
pub struct BlockMap(pub HashMap<BasicBlockId, Block>);
|
||||
|
||||
impl BlockMap {
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
pub fn get(&self, bb: &BasicBlockId) -> Option<&Block> {
|
||||
self.0.get(bb)
|
||||
}
|
||||
pub fn insert(&mut self, bb: BasicBlockId, blk: Block) {
|
||||
self.0.insert(bb, blk);
|
||||
}
|
||||
/// Create a CLIF block for each MIR block id
|
||||
pub fn create_for_function(func: &MirFunction, builder: &mut FunctionBuilder) -> Self {
|
||||
let mut m = HashMap::new();
|
||||
for (bb_id, _) in &func.blocks {
|
||||
m.insert(*bb_id, builder.create_block());
|
||||
}
|
||||
Self(m)
|
||||
}
|
||||
}
|
||||
|
||||
/// Value environment for CLIF lowering: holds current SSA values and pseudo-memory for Load/Store
|
||||
pub struct ValueEnv {
|
||||
vals: HashMap<ValueId, cranelift_codegen::ir::Value>,
|
||||
mem: HashMap<ValueId, cranelift_codegen::ir::Value>,
|
||||
}
|
||||
|
||||
impl ValueEnv {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vals: HashMap::new(),
|
||||
mem: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_val(&self, id: &ValueId) -> Result<cranelift_codegen::ir::Value, String> {
|
||||
self.vals
|
||||
.get(id)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("undef {:?}", id))
|
||||
}
|
||||
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
|
||||
self.vals.insert(id, v);
|
||||
}
|
||||
pub fn get_mem_or(
|
||||
&self,
|
||||
id: &ValueId,
|
||||
default: cranelift_codegen::ir::Value,
|
||||
) -> cranelift_codegen::ir::Value {
|
||||
*self.mem.get(id).unwrap_or(&default)
|
||||
}
|
||||
pub fn set_mem(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
|
||||
self.mem.insert(id, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Cranelift JIT module wrapper (context)
|
||||
pub struct ClifContext {
|
||||
pub module: JITModule,
|
||||
}
|
||||
|
||||
impl ClifContext {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
|
||||
let flag_builder = cranelift_codegen::settings::builder();
|
||||
let flags = cranelift_codegen::settings::Flags::new(flag_builder);
|
||||
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
|
||||
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||
Ok(Self {
|
||||
module: JITModule::new(jit_builder),
|
||||
})
|
||||
}
|
||||
|
||||
/// Declare an exported i64-return function and return its id and Cranelift context/signature
|
||||
pub fn declare_i64_fn(
|
||||
&mut self,
|
||||
name: &str,
|
||||
) -> Result<(FuncId, cranelift_codegen::Context, Signature), String> {
|
||||
let mut sig = Signature::new(self.module.target_config().default_call_conv);
|
||||
sig.returns.push(AbiParam::new(types::I64));
|
||||
let func_id = self
|
||||
.module
|
||||
.declare_function(name, Linkage::Export, &sig)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut ctx = self.module.make_context();
|
||||
ctx.func.signature = sig.clone();
|
||||
Ok((func_id, ctx, sig))
|
||||
}
|
||||
|
||||
pub fn finalize(
|
||||
&mut self,
|
||||
func_id: FuncId,
|
||||
ctx: &mut cranelift_codegen::Context,
|
||||
) -> Result<*const u8, String> {
|
||||
self.module
|
||||
.define_function(func_id, ctx)
|
||||
.map_err(|e| e.to_string())?;
|
||||
self.module.clear_context(ctx);
|
||||
let _ = self.module.finalize_definitions();
|
||||
Ok(self.module.get_finalized_function(func_id))
|
||||
}
|
||||
}
|
||||
185
archive/jit-cranelift/src/backend/cranelift/jit.rs
Normal file
185
archive/jit-cranelift/src/backend/cranelift/jit.rs
Normal file
@ -0,0 +1,185 @@
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
use cranelift_codegen::ir::{
|
||||
condcodes::IntCC, types, AbiParam, InstBuilder, Signature, StackSlot, StackSlotData,
|
||||
StackSlotKind,
|
||||
};
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
use cranelift_module::{Linkage, Module};
|
||||
|
||||
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
|
||||
|
||||
/// Compile a minimal subset of MIR(main) to a native function and execute it.
|
||||
/// Supported: Const(Integer), BinOp(Add for integers), Return(Integer or default 0).
|
||||
pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
|
||||
// ISA (native)
|
||||
let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
|
||||
let flag_builder = cranelift_codegen::settings::builder();
|
||||
let flags = cranelift_codegen::settings::Flags::new(flag_builder);
|
||||
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
|
||||
|
||||
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||
let mut module = JITModule::new(jit_builder);
|
||||
|
||||
// Signature: () -> i64
|
||||
let mut sig = Signature::new(module.target_config().default_call_conv);
|
||||
sig.returns
|
||||
.push(AbiParam::new(cranelift_codegen::ir::types::I64));
|
||||
let func_id = module
|
||||
.declare_function("ny_main", Linkage::Export, &sig)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut ctx = module.make_context();
|
||||
ctx.func.signature = sig;
|
||||
let mut fb_ctx = FunctionBuilderContext::new();
|
||||
let mut builder = FunctionBuilder::new(&mut ctx.func, &mut fb_ctx);
|
||||
|
||||
// Prepare blocks for the entire MIR function
|
||||
use std::collections::HashMap;
|
||||
let mut clif_blocks: HashMap<BasicBlockId, cranelift_codegen::ir::Block> = HashMap::new();
|
||||
let mut vals: HashMap<ValueId, cranelift_codegen::ir::Value> = HashMap::new();
|
||||
let mut slots: HashMap<ValueId, StackSlot> = HashMap::new();
|
||||
|
||||
for (bb_id, _) in &main.blocks {
|
||||
clif_blocks.insert(*bb_id, builder.create_block());
|
||||
}
|
||||
// Switch to entry
|
||||
let entry = *clif_blocks.get(&main.entry_block).unwrap();
|
||||
builder.switch_to_block(entry);
|
||||
builder.append_block_params_for_function_params(entry);
|
||||
|
||||
// Emit each block
|
||||
// Deterministic order by id
|
||||
let mut bb_ids: Vec<_> = main.blocks.keys().copied().collect();
|
||||
bb_ids.sort_by_key(|b| b.0);
|
||||
for bb_id in bb_ids {
|
||||
let bb = main.blocks.get(&bb_id).unwrap();
|
||||
let cb = *clif_blocks.get(&bb_id).unwrap();
|
||||
builder.switch_to_block(cb);
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
MirInstruction::Const { dst, value } => {
|
||||
let v = match value {
|
||||
ConstValue::Integer(i) => builder.ins().iconst(types::I64, *i),
|
||||
ConstValue::Bool(b) => {
|
||||
builder.ins().iconst(types::I64, if *b { 1 } else { 0 })
|
||||
}
|
||||
ConstValue::Float(f) => {
|
||||
let fv = builder.ins().f64const(*f);
|
||||
builder.ins().fcvt_to_sint(types::I64, fv)
|
||||
}
|
||||
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => {
|
||||
builder.ins().iconst(types::I64, 0)
|
||||
}
|
||||
};
|
||||
vals.insert(*dst, v);
|
||||
}
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } => {
|
||||
use crate::mir::BinaryOp;
|
||||
let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?;
|
||||
let r = *vals.get(rhs).ok_or_else(|| format!("undef {:?}", rhs))?;
|
||||
let out = match op {
|
||||
BinaryOp::Add => builder.ins().iadd(l, r),
|
||||
BinaryOp::Sub => builder.ins().isub(l, r),
|
||||
BinaryOp::Mul => builder.ins().imul(l, r),
|
||||
BinaryOp::Div => builder.ins().sdiv(l, r),
|
||||
BinaryOp::Mod => builder.ins().srem(l, r),
|
||||
_ => builder.ins().iconst(types::I64, 0),
|
||||
};
|
||||
vals.insert(*dst, out);
|
||||
}
|
||||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||||
let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?;
|
||||
let r = *vals.get(rhs).ok_or_else(|| format!("undef {:?}", rhs))?;
|
||||
let cc = match op {
|
||||
CompareOp::Eq => IntCC::Equal,
|
||||
CompareOp::Ne => IntCC::NotEqual,
|
||||
CompareOp::Lt => IntCC::SignedLessThan,
|
||||
CompareOp::Le => IntCC::SignedLessThanOrEqual,
|
||||
CompareOp::Gt => IntCC::SignedGreaterThan,
|
||||
CompareOp::Ge => IntCC::SignedGreaterThanOrEqual,
|
||||
};
|
||||
let b1 = builder.ins().icmp(cc, l, r);
|
||||
let one = builder.ins().iconst(types::I64, 1);
|
||||
let zero = builder.ins().iconst(types::I64, 0);
|
||||
let i64v = builder.ins().select(b1, one, zero);
|
||||
vals.insert(*dst, i64v);
|
||||
}
|
||||
MirInstruction::Load { dst, ptr } => {
|
||||
if let Some(ss) = slots.get(ptr).copied() {
|
||||
let v = builder.ins().stack_load(types::I64, ss, 0);
|
||||
vals.insert(*dst, v);
|
||||
} else {
|
||||
vals.insert(*dst, builder.ins().iconst(types::I64, 0));
|
||||
}
|
||||
}
|
||||
MirInstruction::Store { value, ptr } => {
|
||||
let v = *vals
|
||||
.get(value)
|
||||
.ok_or_else(|| format!("undef {:?}", value))?;
|
||||
let ss = *slots.entry(*ptr).or_insert_with(|| {
|
||||
builder.create_sized_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
8,
|
||||
))
|
||||
});
|
||||
builder.ins().stack_store(v, ss, 0);
|
||||
}
|
||||
MirInstruction::Copy { dst, src } => {
|
||||
let v = *vals.get(src).ok_or_else(|| format!("undef {:?}", src))?;
|
||||
vals.insert(*dst, v);
|
||||
}
|
||||
_ => { /* ignore unhandled for now */ }
|
||||
}
|
||||
}
|
||||
// Terminator
|
||||
match &bb.terminator {
|
||||
Some(MirInstruction::Return { value }) => {
|
||||
let retv = if let Some(v) = value {
|
||||
*vals.get(v).unwrap_or(&builder.ins().iconst(types::I64, 0))
|
||||
} else {
|
||||
builder.ins().iconst(types::I64, 0)
|
||||
};
|
||||
builder.ins().return_(&[retv]);
|
||||
}
|
||||
Some(MirInstruction::Jump { target }) => {
|
||||
let t = *clif_blocks.get(target).unwrap();
|
||||
builder.ins().jump(t, &[]);
|
||||
}
|
||||
Some(MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
}) => {
|
||||
let cond_i64 = *vals
|
||||
.get(condition)
|
||||
.unwrap_or(&builder.ins().iconst(types::I64, 0));
|
||||
let is_true = builder.ins().icmp_imm(IntCC::NotEqual, cond_i64, 0);
|
||||
let tb = *clif_blocks.get(then_bb).unwrap();
|
||||
let eb = *clif_blocks.get(else_bb).unwrap();
|
||||
builder.ins().brif(is_true, tb, &[], eb, &[]);
|
||||
}
|
||||
_ => {
|
||||
/* fallthrough not allowed: insert return 0 to keep verifier happy */
|
||||
let z = builder.ins().iconst(types::I64, 0);
|
||||
builder.ins().return_(&[z]);
|
||||
}
|
||||
}
|
||||
builder.seal_block(cb);
|
||||
}
|
||||
|
||||
builder.finalize();
|
||||
|
||||
module
|
||||
.define_function(func_id, &mut ctx)
|
||||
.map_err(|e| e.to_string())?;
|
||||
module.clear_context(&mut ctx);
|
||||
let _ = module.finalize_definitions();
|
||||
|
||||
let code = module.get_finalized_function(func_id);
|
||||
let func = unsafe { std::mem::transmute::<_, extern "C" fn() -> i64>(code) };
|
||||
let result = func();
|
||||
Ok(result)
|
||||
}
|
||||
259
archive/jit-cranelift/src/backend/cranelift/mod.rs
Normal file
259
archive/jit-cranelift/src/backend/cranelift/mod.rs
Normal file
@ -0,0 +1,259 @@
|
||||
/*!
|
||||
* Cranelift Backend (skeleton) - Compile MIR to native code for JIT/AOT
|
||||
*
|
||||
* Phase 11.7 kick-off: minimal stubs behind the `cranelift-jit` feature.
|
||||
*/
|
||||
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
|
||||
use crate::jit::lower::{
|
||||
builder::{IRBuilder, NoopBuilder},
|
||||
core::LowerCore,
|
||||
};
|
||||
use crate::jit::semantics::clif::ClifSemanticsSkeleton;
|
||||
use crate::mir::{function::MirModule, BinaryOp, ConstValue, MirInstruction, ValueId};
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::semantics::Semantics;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod builder;
|
||||
pub mod context; // Context/Block/Value env wrappers // Clif IRBuilder implementation (skeleton)
|
||||
pub mod lower {}
|
||||
pub mod jit; // JIT compile/execute using Cranelift (minimal)
|
||||
pub mod object {}
|
||||
|
||||
/// JIT: compile and execute a MIR module (skeleton)
|
||||
pub fn compile_and_execute(
|
||||
mir_module: &MirModule,
|
||||
_temp_name: &str,
|
||||
) -> Result<Box<dyn NyashBox>, String> {
|
||||
// Minimal semantics: Const/Return/Add only (straight-line code)
|
||||
let main = mir_module
|
||||
.functions
|
||||
.get("main")
|
||||
.ok_or("missing main function")?;
|
||||
|
||||
// Minimal ClifSem lowering pass (NoopBuilder): Const/Return/Add カバレッジ確認
|
||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
||||
let mut builder = NoopBuilder::new();
|
||||
let mut lower = LowerCore::new();
|
||||
let _ = lower.lower_function(main, &mut builder);
|
||||
eprintln!(
|
||||
"[CLIF-LOWER] covered={} unsupported={}",
|
||||
lower.covered, lower.unsupported
|
||||
);
|
||||
}
|
||||
|
||||
// まずは新JITエンジン経路を試す(LowerCore -> CraneliftBuilder -> 実行)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
let mut engine = crate::jit::engine::JitEngine::new();
|
||||
if let Some(h) = engine.compile_function(&main.signature.name, main) {
|
||||
// 実行(引数なし)。戻り値は MIR の型に合わせて変換
|
||||
let out = engine.execute_handle(h, &[]);
|
||||
if let Some(jv) = out {
|
||||
let vmv =
|
||||
crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
|
||||
let boxed: Box<dyn NyashBox> = match vmv {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(IntegerBox::new(i)),
|
||||
crate::backend::vm::VMValue::Float(f) => {
|
||||
Box::new(crate::boxes::FloatBox::new(f))
|
||||
}
|
||||
crate::backend::vm::VMValue::Bool(b) => Box::new(BoolBox::new(b)),
|
||||
crate::backend::vm::VMValue::String(s) => Box::new(StringBox::new(&s)),
|
||||
crate::backend::vm::VMValue::BoxRef(b) => b.share_box(),
|
||||
crate::backend::vm::VMValue::Future(fu) => Box::new(fu),
|
||||
crate::backend::vm::VMValue::Void => Box::new(VoidBox::new()),
|
||||
};
|
||||
return Ok(boxed);
|
||||
}
|
||||
}
|
||||
// 失敗した場合はミニマルJITへフォールバック
|
||||
if let Ok(i) = crate::backend::cranelift::jit::compile_and_execute_minimal(main) {
|
||||
return Ok(Box::new(IntegerBox::new(i)));
|
||||
}
|
||||
}
|
||||
let mut regs: HashMap<ValueId, crate::backend::vm::VMValue> = HashMap::new();
|
||||
let mut cur = main.entry_block;
|
||||
let mut last_pred: Option<crate::mir::BasicBlockId> = None;
|
||||
loop {
|
||||
let bb = main
|
||||
.blocks
|
||||
.get(&cur)
|
||||
.ok_or_else(|| format!("invalid bb {:?}", cur))?;
|
||||
// PHI (very minimal): choose first input or predecessor match
|
||||
for inst in &bb.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let Some(pred) = last_pred {
|
||||
if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) {
|
||||
if let Some(val) = regs.get(v).cloned() {
|
||||
regs.insert(*dst, val);
|
||||
}
|
||||
}
|
||||
} else if let Some((_, v)) = inputs.first() {
|
||||
if let Some(val) = regs.get(v).cloned() {
|
||||
regs.insert(*dst, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut sem = ClifSemanticsSkeleton::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
MirInstruction::Const { dst, value } => {
|
||||
let vv = match value {
|
||||
ConstValue::Integer(i) => sem.const_i64(*i),
|
||||
ConstValue::Float(f) => sem.const_f64(*f),
|
||||
ConstValue::Bool(b) => sem.const_bool(*b),
|
||||
ConstValue::String(s) => sem.const_str(s),
|
||||
ConstValue::Null | ConstValue::Void => sem.const_null(),
|
||||
};
|
||||
regs.insert(*dst, vv);
|
||||
}
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
let a = regs
|
||||
.get(lhs)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("undef {:?}", lhs))?;
|
||||
let b = regs
|
||||
.get(rhs)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("undef {:?}", rhs))?;
|
||||
let out = sem.add(a, b);
|
||||
regs.insert(*dst, out);
|
||||
}
|
||||
MirInstruction::Copy { dst, src } => {
|
||||
if let Some(v) = regs.get(src).cloned() {
|
||||
regs.insert(*dst, v);
|
||||
}
|
||||
}
|
||||
MirInstruction::Debug { .. }
|
||||
| MirInstruction::Print { .. }
|
||||
| MirInstruction::Barrier { .. }
|
||||
| MirInstruction::BarrierRead { .. }
|
||||
| MirInstruction::BarrierWrite { .. }
|
||||
| MirInstruction::Safepoint
|
||||
| MirInstruction::Load { .. }
|
||||
| MirInstruction::Store { .. }
|
||||
| MirInstruction::TypeOp { .. }
|
||||
| MirInstruction::Compare { .. }
|
||||
| MirInstruction::NewBox { .. }
|
||||
| MirInstruction::PluginInvoke { .. }
|
||||
| MirInstruction::BoxCall { .. }
|
||||
| MirInstruction::RefGet { .. }
|
||||
| MirInstruction::RefSet { .. }
|
||||
| MirInstruction::WeakRef { .. }
|
||||
| MirInstruction::FutureNew { .. }
|
||||
| MirInstruction::FutureSet { .. }
|
||||
| MirInstruction::Await { .. }
|
||||
| MirInstruction::Throw { .. }
|
||||
| MirInstruction::Catch { .. } => {
|
||||
// ignore for minimal path
|
||||
}
|
||||
MirInstruction::ExternCall {
|
||||
dst,
|
||||
iface_name,
|
||||
method_name,
|
||||
args,
|
||||
..
|
||||
} => {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
match (iface_name.as_str(), method_name.as_str()) {
|
||||
("env.local", "get") => {
|
||||
if let Some(d) = dst {
|
||||
if let Some(a0) = args.get(0) {
|
||||
if let Some(v) = regs.get(a0).cloned() {
|
||||
regs.insert(*d, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
("env.local", "set") => {
|
||||
if args.len() >= 2 {
|
||||
if let Some(v) = regs.get(&args[1]).cloned() {
|
||||
regs.insert(args[0], v);
|
||||
}
|
||||
}
|
||||
// dst ignored
|
||||
}
|
||||
("env.box", "new") => {
|
||||
if let Some(d) = dst {
|
||||
if let Some(a0) = args.get(0) {
|
||||
if let Some(V::String(ty)) = regs.get(a0).cloned() {
|
||||
let reg =
|
||||
crate::runtime::box_registry::get_global_registry();
|
||||
// Collect args as NyashBox
|
||||
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> =
|
||||
Vec::new();
|
||||
for vid in args.iter().skip(1) {
|
||||
if let Some(v) = regs.get(vid).cloned() {
|
||||
ny_args.push(v.to_nyash_box());
|
||||
}
|
||||
}
|
||||
if let Ok(b) = reg.create_box(&ty, &ny_args) {
|
||||
regs.insert(*d, V::from_nyash_box(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { /* ignore other externs in skeleton */ }
|
||||
}
|
||||
}
|
||||
MirInstruction::Phi { .. } => { /* handled above */ }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
match &bb.terminator {
|
||||
Some(MirInstruction::Return { value }) => {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
let vb = match value {
|
||||
Some(v) => regs.get(v).cloned().unwrap_or(V::Void),
|
||||
None => V::Void,
|
||||
};
|
||||
// Box to NyashBox
|
||||
let out: Box<dyn NyashBox> = match vb {
|
||||
V::Integer(i) => Box::new(IntegerBox::new(i)),
|
||||
V::Bool(b) => Box::new(BoolBox::new(b)),
|
||||
V::String(s) => Box::new(StringBox::new(&s)),
|
||||
V::Float(f) => Box::new(crate::boxes::FloatBox::new(f)),
|
||||
V::Void => Box::new(VoidBox::new()),
|
||||
V::Future(fu) => Box::new(fu),
|
||||
V::BoxRef(b) => b.share_box(),
|
||||
};
|
||||
return Ok(out);
|
||||
}
|
||||
Some(MirInstruction::Jump { target }) => {
|
||||
last_pred = Some(bb.id);
|
||||
cur = *target;
|
||||
}
|
||||
Some(MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
}) => {
|
||||
// Minimal: integer/bool truthiness
|
||||
let c = regs
|
||||
.get(condition)
|
||||
.cloned()
|
||||
.unwrap_or(crate::backend::vm::VMValue::Void);
|
||||
let t = match c {
|
||||
crate::backend::vm::VMValue::Bool(b) => b,
|
||||
crate::backend::vm::VMValue::Integer(i) => i != 0,
|
||||
_ => false,
|
||||
};
|
||||
last_pred = Some(bb.id);
|
||||
cur = if t { *then_bb } else { *else_bb };
|
||||
}
|
||||
Some(other) => return Err(format!("unsupported terminator {:?}", other)),
|
||||
None => return Err(format!("unterminated block {:?}", bb.id)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// AOT: compile to object file (not yet implemented in skeleton)
|
||||
pub fn compile_to_object(_mir_module: &MirModule, _out_path: &str) -> Result<(), String> {
|
||||
Err("Cranelift AOT emit not implemented (skeleton)".to_string())
|
||||
}
|
||||
213
archive/jit-cranelift/src/boxes/jit_config_box.rs
Normal file
213
archive/jit-cranelift/src/boxes/jit_config_box.rs
Normal file
@ -0,0 +1,213 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox};
|
||||
use crate::interpreter::RuntimeError;
|
||||
use crate::jit::config::JitConfig;
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JitConfigBox {
|
||||
base: BoxBase,
|
||||
pub config: RwLock<JitConfig>,
|
||||
}
|
||||
|
||||
impl JitConfigBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
config: RwLock::new(JitConfig::from_env()),
|
||||
}
|
||||
}
|
||||
/// Update internal config flags from runtime capability probe
|
||||
pub fn from_runtime_probe(&self) -> Box<dyn NyashBox> {
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if cfg.native_bool_abi && !caps.supports_b1_sig {
|
||||
cfg.native_bool_abi = false;
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_flag(&self, name: &str, on: bool) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
match name {
|
||||
"exec" => cfg.exec = on,
|
||||
"stats" => cfg.stats = on,
|
||||
"stats_json" => cfg.stats_json = on,
|
||||
"dump" => cfg.dump = on,
|
||||
"phi_min" => cfg.phi_min = on,
|
||||
"hostcall" => cfg.hostcall = on,
|
||||
"handle_debug" => cfg.handle_debug = on,
|
||||
"native_f64" => cfg.native_f64 = on,
|
||||
"native_bool" => cfg.native_bool = on,
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi = on,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1 = on,
|
||||
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric = on,
|
||||
_ => {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown flag: {}", name),
|
||||
})
|
||||
}
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn get_flag(&self, name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let b = match name {
|
||||
"exec" => cfg.exec,
|
||||
"stats" => cfg.stats,
|
||||
"stats_json" => cfg.stats_json,
|
||||
"dump" => cfg.dump,
|
||||
"phi_min" => cfg.phi_min,
|
||||
"hostcall" => cfg.hostcall,
|
||||
"handle_debug" => cfg.handle_debug,
|
||||
"native_f64" => cfg.native_f64,
|
||||
"native_bool" => cfg.native_bool,
|
||||
"bool_abi" | "native_bool_abi" => cfg.native_bool_abi,
|
||||
"ret_b1" | "ret_bool_b1" => cfg.ret_bool_b1,
|
||||
"relax_numeric" | "hostcall_relax_numeric" => cfg.relax_numeric,
|
||||
_ => {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown flag: {}", name),
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok(Box::new(BoolBox::new(b)))
|
||||
}
|
||||
pub fn set_threshold(&self, v: i64) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
if v <= 0 {
|
||||
cfg.threshold = None;
|
||||
} else {
|
||||
cfg.threshold = Some(v as u32);
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn get_threshold(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
Box::new(IntegerBox::new(
|
||||
cfg.threshold.map(|v| v as i64).unwrap_or(0),
|
||||
))
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let val = serde_json::json!({
|
||||
"exec": cfg.exec,
|
||||
"stats": cfg.stats,
|
||||
"stats_json": cfg.stats_json,
|
||||
"dump": cfg.dump,
|
||||
"threshold": cfg.threshold,
|
||||
"phi_min": cfg.phi_min,
|
||||
"hostcall": cfg.hostcall,
|
||||
"handle_debug": cfg.handle_debug,
|
||||
"native_f64": cfg.native_f64,
|
||||
"native_bool": cfg.native_bool,
|
||||
"native_bool_abi": cfg.native_bool_abi,
|
||||
"ret_bool_b1": cfg.ret_bool_b1,
|
||||
"relax_numeric": cfg.relax_numeric,
|
||||
});
|
||||
Box::new(StringBox::new(val.to_string()))
|
||||
}
|
||||
pub fn from_json(&self, s: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
let v: serde_json::Value =
|
||||
serde_json::from_str(s).map_err(|e| RuntimeError::InvalidOperation {
|
||||
message: format!("Invalid JSON: {}", e),
|
||||
})?;
|
||||
if let Some(b) = v.get("exec").and_then(|x| x.as_bool()) {
|
||||
cfg.exec = b;
|
||||
}
|
||||
if let Some(b) = v.get("stats").and_then(|x| x.as_bool()) {
|
||||
cfg.stats = b;
|
||||
}
|
||||
if let Some(b) = v.get("stats_json").and_then(|x| x.as_bool()) {
|
||||
cfg.stats_json = b;
|
||||
}
|
||||
if let Some(b) = v.get("dump").and_then(|x| x.as_bool()) {
|
||||
cfg.dump = b;
|
||||
}
|
||||
if let Some(n) = v.get("threshold").and_then(|x| x.as_u64()) {
|
||||
cfg.threshold = Some(n as u32);
|
||||
}
|
||||
if let Some(b) = v.get("phi_min").and_then(|x| x.as_bool()) {
|
||||
cfg.phi_min = b;
|
||||
}
|
||||
if let Some(b) = v.get("hostcall").and_then(|x| x.as_bool()) {
|
||||
cfg.hostcall = b;
|
||||
}
|
||||
if let Some(b) = v.get("handle_debug").and_then(|x| x.as_bool()) {
|
||||
cfg.handle_debug = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_f64").and_then(|x| x.as_bool()) {
|
||||
cfg.native_f64 = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_bool").and_then(|x| x.as_bool()) {
|
||||
cfg.native_bool = b;
|
||||
}
|
||||
if let Some(b) = v.get("native_bool_abi").and_then(|x| x.as_bool()) {
|
||||
cfg.native_bool_abi = b;
|
||||
}
|
||||
if let Some(b) = v.get("ret_bool_b1").and_then(|x| x.as_bool()) {
|
||||
cfg.ret_bool_b1 = b;
|
||||
}
|
||||
if let Some(b) = v.get("relax_numeric").and_then(|x| x.as_bool()) {
|
||||
cfg.relax_numeric = b;
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
pub fn apply(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
// Apply to env for CLI parity
|
||||
cfg.apply_env();
|
||||
// Also set global current JIT config for hot paths (env-less)
|
||||
crate::jit::config::set_current(cfg);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn summary(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap();
|
||||
let s = format!(
|
||||
"exec={} stats={} json={} dump={} thr={:?} phi_min={} hostcall={} hdbg={} f64={} bool={} relax_numeric={}",
|
||||
cfg.exec, cfg.stats, cfg.stats_json, cfg.dump, cfg.threshold,
|
||||
cfg.phi_min, cfg.hostcall, cfg.handle_debug, cfg.native_f64, cfg.native_bool, cfg.relax_numeric
|
||||
);
|
||||
Box::new(StringBox::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitConfigBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitConfigBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitConfigBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.summary().to_string_box().value)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitConfigBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitConfigBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = self.config.read().unwrap().clone();
|
||||
Box::new(JitConfigBox {
|
||||
base: self.base.clone(),
|
||||
config: RwLock::new(cfg),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
74
archive/jit-cranelift/src/boxes/jit_events_box.rs
Normal file
74
archive/jit-cranelift/src/boxes/jit_events_box.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitEventsBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitEventsBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitEventsBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitEventsBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitEventsBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitEventsBox")
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitEventsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitEventsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitEventsBox {
|
||||
pub fn set_path(&self, path: &str) -> Box<dyn NyashBox> {
|
||||
std::env::set_var("NYASH_JIT_EVENTS_PATH", path);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_EVENTS", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_EVENTS");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn log(&self, kind: &str, function: &str, note_json: &str) -> Box<dyn NyashBox> {
|
||||
let extra = serde_json::from_str::<serde_json::Value>(note_json)
|
||||
.unwrap_or_else(|_| serde_json::json!({"note": note_json}));
|
||||
crate::jit::events::emit(kind, function, None, None, extra);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
78
archive/jit-cranelift/src/boxes/jit_hostcall_registry_box.rs
Normal file
78
archive/jit-cranelift/src/boxes/jit_hostcall_registry_box.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitHostcallRegistryBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitHostcallRegistryBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitHostcallRegistryBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitHostcallRegistryBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitHostcallRegistryBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let (ro, mu) = crate::jit::hostcall_registry::snapshot();
|
||||
let payload = serde_json::json!({ "readonly": ro, "mutating": mu });
|
||||
StringBox::new(payload.to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitHostcallRegistryBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitHostcallRegistryBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitHostcallRegistryBox {
|
||||
pub fn add_readonly(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_readonly(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_mutating(&self, sym: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::add_mutating(sym);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_from_csv(&self, ro_csv: &str, mu_csv: &str) -> Box<dyn NyashBox> {
|
||||
crate::jit::hostcall_registry::set_from_csv(ro_csv, mu_csv);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn add_signature(&self, sym: &str, args_csv: &str, ret_str: &str) -> Box<dyn NyashBox> {
|
||||
let ok = crate::jit::hostcall_registry::set_signature_csv(sym, args_csv, ret_str);
|
||||
if ok {
|
||||
Box::new(VoidBox::new())
|
||||
} else {
|
||||
Box::new(StringBox::new("Invalid signature"))
|
||||
}
|
||||
}
|
||||
}
|
||||
144
archive/jit-cranelift/src/boxes/jit_policy_box.rs
Normal file
144
archive/jit-cranelift/src/boxes/jit_policy_box.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitPolicyBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitPolicyBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitPolicyBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitPolicyBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitPolicyBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
let p = crate::jit::policy::current();
|
||||
let s = format!(
|
||||
"read_only={} whitelist={}",
|
||||
p.read_only,
|
||||
p.hostcall_whitelist.join(",")
|
||||
);
|
||||
StringBox::new(s)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitPolicyBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitPolicyBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
// Methods (exposed via VM dispatch):
|
||||
impl JitPolicyBox {
|
||||
pub fn set_flag(&self, name: &str, on: bool) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
match name {
|
||||
"read_only" | "readonly" => cur.read_only = on,
|
||||
_ => return Box::new(StringBox::new(format!("Unknown flag: {}", name))),
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn get_flag(&self, name: &str) -> Box<dyn NyashBox> {
|
||||
let cur = crate::jit::policy::current();
|
||||
let v = match name {
|
||||
"read_only" | "readonly" => cur.read_only,
|
||||
_ => false,
|
||||
};
|
||||
Box::new(BoolBox::new(v))
|
||||
}
|
||||
pub fn set_whitelist_csv(&self, csv: &str) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
cur.hostcall_whitelist = csv
|
||||
.split(',')
|
||||
.map(|t| t.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn add_whitelist(&self, name: &str) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
if !cur.hostcall_whitelist.iter().any(|s| s == name) {
|
||||
cur.hostcall_whitelist.push(name.to_string());
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn clear_whitelist(&self) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
cur.hostcall_whitelist.clear();
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn enable_preset(&self, name: &str) -> Box<dyn NyashBox> {
|
||||
let mut cur = crate::jit::policy::current();
|
||||
match name {
|
||||
// 最小: Array.push_h のみ許可(読み取り以外は変えない)
|
||||
"mutating_minimal" | "mutating_array_push" => {
|
||||
let id = crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H;
|
||||
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
|
||||
cur.hostcall_whitelist.push(id.to_string());
|
||||
}
|
||||
}
|
||||
// 例: Map.set_h も追加許可(必要に応じて拡張)
|
||||
"mutating_map_set" => {
|
||||
let id = crate::jit::r#extern::collections::SYM_MAP_SET_H;
|
||||
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
|
||||
cur.hostcall_whitelist.push(id.to_string());
|
||||
}
|
||||
}
|
||||
// よく使う: Array.push_h + Array.set_h + Map.set_h を許可
|
||||
"mutating_common" => {
|
||||
let ids = [
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H,
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
|
||||
crate::jit::r#extern::collections::SYM_MAP_SET_H,
|
||||
];
|
||||
for id in ids {
|
||||
if !cur.hostcall_whitelist.iter().any(|s| s == id) {
|
||||
cur.hostcall_whitelist.push(id.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Box::new(StringBox::new(format!("Unknown preset: {}", name)));
|
||||
}
|
||||
}
|
||||
crate::jit::policy::set_current(cur);
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
71
archive/jit-cranelift/src/boxes/jit_stats_box.rs
Normal file
71
archive/jit-cranelift/src/boxes/jit_stats_box.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitStatsBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitStatsBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
pub fn to_json(&self) -> Box<dyn NyashBox> {
|
||||
let cfg = crate::jit::config::current();
|
||||
let caps = crate::jit::config::probe_capabilities();
|
||||
let mode = if cfg.native_bool_abi && caps.supports_b1_sig {
|
||||
"b1_bool"
|
||||
} else {
|
||||
"i64_bool"
|
||||
};
|
||||
let payload = serde_json::json!({
|
||||
"version": 1,
|
||||
"abi_mode": mode,
|
||||
"abi_b1_enabled": cfg.native_bool_abi,
|
||||
"abi_b1_supported": caps.supports_b1_sig,
|
||||
"b1_norm_count": crate::jit::rt::b1_norm_get(),
|
||||
"ret_bool_hint_count": crate::jit::rt::ret_bool_hint_get(),
|
||||
"phi_total_slots": crate::jit::rt::phi_total_get(),
|
||||
"phi_b1_slots": crate::jit::rt::phi_b1_get(),
|
||||
});
|
||||
Box::new(StringBox::new(payload.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitStatsBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitStatsBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitStatsBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(self.to_json().to_string_box().value)
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitStatsBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStatsBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
104
archive/jit-cranelift/src/boxes/jit_strict_box.rs
Normal file
104
archive/jit-cranelift/src/boxes/jit_strict_box.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox, VoidBox};
|
||||
use std::any::Any;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JitStrictBox {
|
||||
base: BoxBase,
|
||||
}
|
||||
|
||||
impl JitStrictBox {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: BoxBase::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxCore for JitStrictBox {
|
||||
fn box_id(&self) -> u64 {
|
||||
self.base.id
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
self.base.parent_type_id
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "JitStrictBox")
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for JitStrictBox {
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new("JitStrictBox".to_string())
|
||||
}
|
||||
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
|
||||
BoolBox::new(other.as_any().is::<JitStrictBox>())
|
||||
}
|
||||
fn type_name(&self) -> &'static str {
|
||||
"JitStrictBox"
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(Self {
|
||||
base: self.base.clone(),
|
||||
})
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl JitStrictBox {
|
||||
/// Enable/disable strict mode. When enabling, also set JIT-only and args-handle-only by default.
|
||||
pub fn enable(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_STRICT", "1");
|
||||
if std::env::var("NYASH_JIT_ONLY").ok().is_none() {
|
||||
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||
}
|
||||
if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() {
|
||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||
}
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_STRICT");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn set_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
pub fn set_handle_only(&self, on: bool) -> Box<dyn NyashBox> {
|
||||
if on {
|
||||
std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1");
|
||||
} else {
|
||||
std::env::remove_var("NYASH_JIT_ARGS_HANDLE_ONLY");
|
||||
}
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
|
||||
pub fn status(&self) -> Box<dyn NyashBox> {
|
||||
let s = serde_json::json!({
|
||||
"strict": std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1"),
|
||||
"jit_only": std::env::var("NYASH_JIT_ONLY").ok().as_deref() == Some("1"),
|
||||
"args_handle_only": std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() == Some("1"),
|
||||
"lower_fallbacks": crate::jit::events::lower_fallbacks_get(),
|
||||
});
|
||||
Box::new(StringBox::new(s.to_string()))
|
||||
}
|
||||
|
||||
/// Reset compile-time counters (e.g., lower fallback count) before next compile.
|
||||
pub fn reset_counters(&self) -> Box<dyn NyashBox> {
|
||||
crate::jit::events::lower_counters_reset();
|
||||
Box::new(VoidBox::new())
|
||||
}
|
||||
}
|
||||
72
archive/jit-cranelift/src/jit/abi.rs
Normal file
72
archive/jit-cranelift/src/jit/abi.rs
Normal file
@ -0,0 +1,72 @@
|
||||
//! JIT minimal ABI types independent from VM internals
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum JitValue {
|
||||
I64(i64),
|
||||
F64(f64),
|
||||
Bool(bool),
|
||||
/// Opaque handle for host objects (future use)
|
||||
Handle(u64),
|
||||
}
|
||||
|
||||
impl JitValue {
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
if let JitValue::I64(v) = self {
|
||||
Some(*v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter between VMValue and JitValue — keeps JIT decoupled from VM internals
|
||||
pub mod adapter {
|
||||
use super::JitValue;
|
||||
use crate::backend::vm::VMValue;
|
||||
|
||||
pub fn to_jit_values(args: &[VMValue]) -> Vec<JitValue> {
|
||||
args.iter()
|
||||
.map(|v| match v {
|
||||
VMValue::Integer(i) => JitValue::I64(*i),
|
||||
VMValue::Float(f) => JitValue::F64(*f),
|
||||
VMValue::Bool(b) => JitValue::Bool(*b),
|
||||
VMValue::BoxRef(arc) => {
|
||||
let h = crate::jit::rt::handles::to_handle(arc.clone());
|
||||
JitValue::Handle(h)
|
||||
}
|
||||
// For now, map others to handle via boxing where reasonable
|
||||
VMValue::String(s) => {
|
||||
let bx = Box::new(crate::box_trait::StringBox::new(s));
|
||||
let bx_dyn: Box<dyn crate::box_trait::NyashBox> = bx;
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> =
|
||||
std::sync::Arc::from(bx_dyn);
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
JitValue::Handle(h)
|
||||
}
|
||||
VMValue::Void => JitValue::Handle(0),
|
||||
VMValue::Future(f) => {
|
||||
let bx_dyn: Box<dyn crate::box_trait::NyashBox> = Box::new(f.clone());
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> =
|
||||
std::sync::Arc::from(bx_dyn);
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
JitValue::Handle(h)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn from_jit_value(v: JitValue) -> VMValue {
|
||||
match v {
|
||||
JitValue::I64(i) => VMValue::Integer(i),
|
||||
JitValue::F64(f) => VMValue::Float(f),
|
||||
JitValue::Bool(b) => VMValue::Bool(b),
|
||||
JitValue::Handle(h) => {
|
||||
if let Some(arc) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::BoxRef(arc)
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
archive/jit-cranelift/src/jit/boundary.rs
Normal file
85
archive/jit-cranelift/src/jit/boundary.rs
Normal file
@ -0,0 +1,85 @@
|
||||
//! CallBoundaryBox: unify JIT→VM return conversion in one place
|
||||
use super::abi::JitValue;
|
||||
use crate::backend::vm::VMValue;
|
||||
|
||||
pub struct CallBoundaryBox;
|
||||
|
||||
impl CallBoundaryBox {
|
||||
pub fn to_vm(ret_ty: &crate::mir::MirType, v: JitValue) -> VMValue {
|
||||
match ret_ty {
|
||||
crate::mir::MirType::Float => match v {
|
||||
JitValue::F64(f) => VMValue::Float(f),
|
||||
JitValue::I64(i) => VMValue::Float(i as f64),
|
||||
JitValue::Bool(b) => VMValue::Float(if b { 1.0 } else { 0.0 }),
|
||||
JitValue::Handle(h) => {
|
||||
if let Some(_) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::Float(0.0)
|
||||
} else {
|
||||
VMValue::Float(0.0)
|
||||
}
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::Integer => match v {
|
||||
JitValue::I64(i) => VMValue::Integer(i),
|
||||
JitValue::F64(f) => VMValue::Integer(f as i64),
|
||||
JitValue::Bool(b) => VMValue::Integer(if b { 1 } else { 0 }),
|
||||
JitValue::Handle(h) => {
|
||||
if let Some(_) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::Integer(0)
|
||||
} else {
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::Bool => match v {
|
||||
JitValue::Bool(b) => VMValue::Bool(b),
|
||||
JitValue::I64(i) => VMValue::Bool(i != 0),
|
||||
JitValue::F64(f) => VMValue::Bool(f != 0.0),
|
||||
JitValue::Handle(h) => {
|
||||
if let Some(_) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::Bool(true)
|
||||
} else {
|
||||
VMValue::Bool(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
// Box-like returns: if we received a handle id (encoded as I64), resolve to BoxRef; also honor explicit Handle
|
||||
crate::mir::MirType::Box(_)
|
||||
| crate::mir::MirType::String
|
||||
| crate::mir::MirType::Array(_)
|
||||
| crate::mir::MirType::Future(_) => match v {
|
||||
JitValue::I64(i) => {
|
||||
let h = i as u64;
|
||||
if let Some(arc) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::BoxRef(arc)
|
||||
} else {
|
||||
VMValue::Integer(i)
|
||||
}
|
||||
}
|
||||
JitValue::Handle(h) => {
|
||||
if let Some(arc) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::BoxRef(arc)
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
JitValue::F64(f) => VMValue::Float(f),
|
||||
JitValue::Bool(b) => VMValue::Bool(b),
|
||||
},
|
||||
_ => {
|
||||
// Default adapter with heuristic: treat I64 matching a known handle as BoxRef
|
||||
match v {
|
||||
JitValue::I64(i) => {
|
||||
let h = i as u64;
|
||||
if let Some(arc) = crate::jit::rt::handles::get(h) {
|
||||
VMValue::BoxRef(arc)
|
||||
} else {
|
||||
super::abi::adapter::from_jit_value(JitValue::I64(i))
|
||||
}
|
||||
}
|
||||
_ => super::abi::adapter::from_jit_value(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
archive/jit-cranelift/src/jit/config.rs
Normal file
129
archive/jit-cranelift/src/jit/config.rs
Normal file
@ -0,0 +1,129 @@
|
||||
//! JIT configuration aggregator
|
||||
//!
|
||||
//! Centralizes JIT-related flags so callers and tests can use a single
|
||||
//! source of truth instead of scattering env access across modules.
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct JitConfig {
|
||||
pub exec: bool, // NYASH_JIT_EXEC
|
||||
pub stats: bool, // NYASH_JIT_STATS
|
||||
pub stats_json: bool, // NYASH_JIT_STATS_JSON
|
||||
pub dump: bool, // NYASH_JIT_DUMP
|
||||
pub threshold: Option<u32>, // NYASH_JIT_THRESHOLD
|
||||
pub phi_min: bool, // NYASH_JIT_PHI_MIN
|
||||
pub hostcall: bool, // NYASH_JIT_HOSTCALL
|
||||
pub handle_debug: bool, // NYASH_JIT_HANDLE_DEBUG
|
||||
pub native_f64: bool, // NYASH_JIT_NATIVE_F64
|
||||
pub native_bool: bool, // NYASH_JIT_NATIVE_BOOL
|
||||
pub native_bool_abi: bool, // NYASH_JIT_ABI_B1 (experimental)
|
||||
pub ret_bool_b1: bool, // NYASH_JIT_RET_B1 (footing; currently returns i64 0/1)
|
||||
pub relax_numeric: bool, // NYASH_JIT_HOSTCALL_RELAX_NUMERIC (i64->f64 coercion)
|
||||
}
|
||||
|
||||
impl JitConfig {
|
||||
pub fn from_env() -> Self {
|
||||
let getb = |k: &str| std::env::var(k).ok().as_deref() == Some("1");
|
||||
let threshold = std::env::var("NYASH_JIT_THRESHOLD")
|
||||
.ok()
|
||||
.and_then(|s| s.parse::<u32>().ok());
|
||||
// Respect explicit dump flag, but also treat a non-empty NYASH_JIT_DOT path
|
||||
// as an implicit request to enable dump (so Box/CLI/env stay consistent).
|
||||
let dump_flag = getb("NYASH_JIT_DUMP")
|
||||
|| std::env::var("NYASH_JIT_DOT")
|
||||
.ok()
|
||||
.map(|s| !s.is_empty())
|
||||
.unwrap_or(false);
|
||||
Self {
|
||||
exec: getb("NYASH_JIT_EXEC"),
|
||||
stats: getb("NYASH_JIT_STATS"),
|
||||
stats_json: getb("NYASH_JIT_STATS_JSON"),
|
||||
dump: dump_flag,
|
||||
threshold,
|
||||
phi_min: getb("NYASH_JIT_PHI_MIN"),
|
||||
hostcall: getb("NYASH_JIT_HOSTCALL"),
|
||||
handle_debug: getb("NYASH_JIT_HANDLE_DEBUG"),
|
||||
native_f64: getb("NYASH_JIT_NATIVE_F64"),
|
||||
native_bool: getb("NYASH_JIT_NATIVE_BOOL"),
|
||||
native_bool_abi: getb("NYASH_JIT_ABI_B1"),
|
||||
ret_bool_b1: getb("NYASH_JIT_RET_B1"),
|
||||
relax_numeric: getb("NYASH_JIT_HOSTCALL_RELAX_NUMERIC"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply current struct values into environment variables.
|
||||
/// This keeps existing env untouched unless the value is explicitly set here.
|
||||
pub fn apply_env(&self) {
|
||||
let setb = |k: &str, v: bool| {
|
||||
if v {
|
||||
std::env::set_var(k, "1");
|
||||
}
|
||||
};
|
||||
setb("NYASH_JIT_EXEC", self.exec);
|
||||
setb("NYASH_JIT_STATS", self.stats);
|
||||
setb("NYASH_JIT_STATS_JSON", self.stats_json);
|
||||
setb("NYASH_JIT_DUMP", self.dump);
|
||||
if let Some(t) = self.threshold {
|
||||
std::env::set_var("NYASH_JIT_THRESHOLD", t.to_string());
|
||||
}
|
||||
setb("NYASH_JIT_PHI_MIN", self.phi_min);
|
||||
setb("NYASH_JIT_HOSTCALL", self.hostcall);
|
||||
setb("NYASH_JIT_HANDLE_DEBUG", self.handle_debug);
|
||||
setb("NYASH_JIT_NATIVE_F64", self.native_f64);
|
||||
setb("NYASH_JIT_NATIVE_BOOL", self.native_bool);
|
||||
setb("NYASH_JIT_ABI_B1", self.native_bool_abi);
|
||||
setb("NYASH_JIT_RET_B1", self.ret_bool_b1);
|
||||
setb("NYASH_JIT_HOSTCALL_RELAX_NUMERIC", self.relax_numeric);
|
||||
}
|
||||
}
|
||||
|
||||
// Global current JIT config (thread-safe), defaults to env when unset
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::RwLock;
|
||||
|
||||
static GLOBAL_JIT_CONFIG: OnceCell<RwLock<JitConfig>> = OnceCell::new();
|
||||
|
||||
/// Get current JIT config (falls back to env-derived default if unset)
|
||||
pub fn current() -> JitConfig {
|
||||
if let Some(lock) = GLOBAL_JIT_CONFIG.get() {
|
||||
if let Ok(cfg) = lock.read() {
|
||||
return cfg.clone();
|
||||
}
|
||||
}
|
||||
JitConfig::from_env()
|
||||
}
|
||||
|
||||
/// Set current JIT config (overrides env lookups in hot paths)
|
||||
pub fn set_current(cfg: JitConfig) {
|
||||
if let Some(lock) = GLOBAL_JIT_CONFIG.get() {
|
||||
if let Ok(mut w) = lock.write() {
|
||||
*w = cfg;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let _ = GLOBAL_JIT_CONFIG.set(RwLock::new(cfg));
|
||||
}
|
||||
|
||||
// --- Runtime capability probing (minimal, safe defaults) ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct JitCapabilities {
|
||||
pub supports_b1_sig: bool,
|
||||
}
|
||||
|
||||
/// Probe JIT backend capabilities once. Safe default: b1 signatures are unsupported.
|
||||
pub fn probe_capabilities() -> JitCapabilities {
|
||||
// Current toolchain: allow forcing via env for experiments; otherwise false.
|
||||
// When upgrading Cranelift to a version with B1 signature support, set NYASH_JIT_ABI_B1_SUPPORT=1
|
||||
let forced = std::env::var("NYASH_JIT_ABI_B1_SUPPORT").ok().as_deref() == Some("1");
|
||||
JitCapabilities {
|
||||
supports_b1_sig: forced,
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply runtime capabilities onto a JitConfig (e.g., disable b1 ABI when unsupported)
|
||||
pub fn apply_runtime_caps(mut cfg: JitConfig, caps: JitCapabilities) -> JitConfig {
|
||||
if cfg.native_bool_abi && !caps.supports_b1_sig {
|
||||
cfg.native_bool_abi = false;
|
||||
}
|
||||
cfg
|
||||
}
|
||||
343
archive/jit-cranelift/src/jit/engine.rs
Normal file
343
archive/jit-cranelift/src/jit/engine.rs
Normal file
@ -0,0 +1,343 @@
|
||||
//! JIT Engine skeleton
|
||||
//!
|
||||
//! Phase 10_a: Provide a placeholder engine interface that later hosts
|
||||
//! Cranelift contexts and compiled function handles.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct JitEngine {
|
||||
// In the future: isa, module, context, fn table, etc.
|
||||
#[allow(dead_code)]
|
||||
initialized: bool,
|
||||
#[allow(dead_code)]
|
||||
next_handle: u64,
|
||||
/// Stub function table: handle -> callable closure
|
||||
fntab: HashMap<
|
||||
u64,
|
||||
Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>,
|
||||
>,
|
||||
/// Host externs by symbol name (Phase 10_d)
|
||||
externs: HashMap<
|
||||
String,
|
||||
Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>,
|
||||
>,
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
isa: Option<cranelift_codegen::isa::OwnedTargetIsa>,
|
||||
// Last lower stats (per function)
|
||||
last_phi_total: u64,
|
||||
last_phi_b1: u64,
|
||||
last_ret_bool_hint: bool,
|
||||
}
|
||||
|
||||
impl JitEngine {
|
||||
pub fn new() -> Self {
|
||||
let mut this = Self {
|
||||
initialized: true,
|
||||
next_handle: 1,
|
||||
fntab: HashMap::new(),
|
||||
externs: HashMap::new(),
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
isa: None,
|
||||
last_phi_total: 0,
|
||||
last_phi_b1: 0,
|
||||
last_ret_bool_hint: false,
|
||||
};
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
this.isa = None;
|
||||
}
|
||||
this.register_default_externs();
|
||||
this
|
||||
}
|
||||
|
||||
/// Compile a function if supported; returns an opaque handle id
|
||||
pub fn compile_function(
|
||||
&mut self,
|
||||
func_name: &str,
|
||||
mir: &crate::mir::MirFunction,
|
||||
) -> Option<u64> {
|
||||
let t0 = std::time::Instant::now();
|
||||
// Phase 10_b skeleton: walk MIR with LowerCore and report coverage
|
||||
// Reset compile-phase counters (e.g., fallback decisions) before lowering this function
|
||||
crate::jit::events::lower_counters_reset();
|
||||
let mut lower = crate::jit::lower::core::LowerCore::new();
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
let mut builder = crate::jit::lower::builder::CraneliftBuilder::new();
|
||||
#[cfg(not(feature = "cranelift-jit"))]
|
||||
let mut builder = crate::jit::lower::builder::NoopBuilder::new();
|
||||
if let Err(e) = lower.lower_function(mir, &mut builder) {
|
||||
eprintln!("[JIT] lower failed for {}: {}", func_name, e);
|
||||
return None;
|
||||
}
|
||||
// Strict: fail compile if any fallback decisions were taken during lowering
|
||||
let lower_fallbacks = crate::jit::events::lower_fallbacks_get();
|
||||
if lower_fallbacks > 0 && std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[JIT][strict] lower produced fallback decisions for {}: {} — failing compile",
|
||||
func_name, lower_fallbacks
|
||||
);
|
||||
return None;
|
||||
}
|
||||
// Capture per-function lower stats for manager to query later
|
||||
let (phi_t, phi_b1, ret_b) = lower.last_stats();
|
||||
self.last_phi_total = phi_t;
|
||||
self.last_phi_b1 = phi_b1;
|
||||
self.last_ret_bool_hint = ret_b;
|
||||
// Record per-function stats into manager via callback if available (handled by caller)
|
||||
let cfg_now = crate::jit::config::current();
|
||||
// Strict mode: any unsupported lowering must fail-fast
|
||||
if lower.unsupported > 0 && std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[JIT][strict] unsupported lowering ops for {}: {} — failing compile",
|
||||
func_name, lower.unsupported
|
||||
);
|
||||
return None;
|
||||
}
|
||||
if cfg_now.dump {
|
||||
let phi_min = cfg_now.phi_min;
|
||||
let native_f64 = cfg_now.native_f64;
|
||||
let native_bool = cfg_now.native_bool;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
let s = builder.stats;
|
||||
eprintln!("[JIT] lower {}: argc={} phi_min={} f64={} bool={} covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
|
||||
func_name, mir.params.len(), phi_min, native_f64, native_bool,
|
||||
lower.covered, lower.unsupported,
|
||||
s.0, s.1, s.2, s.3, s.4);
|
||||
}
|
||||
#[cfg(not(feature = "cranelift-jit"))]
|
||||
{
|
||||
eprintln!("[JIT] lower {}: argc={} phi_min={} f64={} bool={} covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
|
||||
func_name, mir.params.len(), phi_min, native_f64, native_bool,
|
||||
lower.covered, lower.unsupported,
|
||||
builder.consts, builder.binops, builder.cmps, builder.branches, builder.rets);
|
||||
}
|
||||
// Optional DOT export
|
||||
if let Ok(path) = std::env::var("NYASH_JIT_DOT") {
|
||||
if !path.is_empty() {
|
||||
if let Err(e) = crate::jit::lower::core::dump_cfg_dot(mir, &path, phi_min) {
|
||||
eprintln!("[JIT] DOT export failed: {}", e);
|
||||
} else {
|
||||
eprintln!("[JIT] DOT written to {}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If lowering left any unsupported instructions, do not register a closure.
|
||||
// This preserves VM semantics until coverage is complete for the function.
|
||||
if lower.unsupported > 0
|
||||
&& std::env::var("NYASH_AOT_ALLOW_UNSUPPORTED").ok().as_deref() != Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") || cfg_now.dump {
|
||||
eprintln!(
|
||||
"[JIT] skip compile for {}: unsupported={} (>0)",
|
||||
func_name, lower.unsupported
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
// Create a handle and register an executable closure if available
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
let h = self.next_handle;
|
||||
self.next_handle = self.next_handle.saturating_add(1);
|
||||
if let Some(closure) = builder.take_compiled_closure() {
|
||||
self.fntab.insert(h, closure);
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
|
||||
let dt = t0.elapsed();
|
||||
eprintln!("[JIT] compile_time_ms={} for {}", dt.as_millis(), func_name);
|
||||
}
|
||||
// Optional: also emit an object file for AOT if requested via env
|
||||
if let Ok(path) = std::env::var("NYASH_AOT_OBJECT_OUT") {
|
||||
if !path.is_empty() {
|
||||
let mut lower2 = crate::jit::lower::core::LowerCore::new();
|
||||
let mut objb = crate::jit::lower::builder::ObjectBuilder::new();
|
||||
if let Err(e) = lower2.lower_function(mir, &mut objb) {
|
||||
eprintln!("[AOT] lower failed for {}: {}", func_name, e);
|
||||
} else if let Some(bytes) = objb.take_object_bytes() {
|
||||
use std::path::Path;
|
||||
let p = Path::new(&path);
|
||||
let out_path = if p.is_dir() || path.ends_with('/') {
|
||||
p.join(format!("{}.o", func_name))
|
||||
} else {
|
||||
p.to_path_buf()
|
||||
};
|
||||
if let Some(parent) = out_path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
match std::fs::write(&out_path, bytes) {
|
||||
Ok(_) => {
|
||||
eprintln!("[AOT] wrote object: {}", out_path.display());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[AOT] failed to write object {}: {}",
|
||||
out_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Some(h);
|
||||
}
|
||||
// If Cranelift path did not produce a closure, treat as not compiled
|
||||
// Even if a closure was not produced, attempt AOT object emission when requested
|
||||
if let Ok(path) = std::env::var("NYASH_AOT_OBJECT_OUT") {
|
||||
if !path.is_empty() {
|
||||
let mut lower2 = crate::jit::lower::core::LowerCore::new();
|
||||
let mut objb = crate::jit::lower::builder::ObjectBuilder::new();
|
||||
match lower2.lower_function(mir, &mut objb) {
|
||||
Err(e) => eprintln!("[AOT] lower failed for {}: {}", func_name, e),
|
||||
Ok(()) => {
|
||||
if let Some(bytes) = objb.take_object_bytes() {
|
||||
use std::path::Path;
|
||||
let p = Path::new(&path);
|
||||
let out_path = if p.is_dir() || path.ends_with('/') {
|
||||
p.join(format!("{}.o", func_name))
|
||||
} else {
|
||||
p.to_path_buf()
|
||||
};
|
||||
if let Some(parent) = out_path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
match std::fs::write(&out_path, bytes) {
|
||||
Ok(_) => {
|
||||
eprintln!("[AOT] wrote object: {}", out_path.display())
|
||||
}
|
||||
Err(e) => eprintln!(
|
||||
"[AOT] failed to write object {}: {}",
|
||||
out_path.display(),
|
||||
e
|
||||
),
|
||||
}
|
||||
} else {
|
||||
eprintln!("[AOT] no object bytes available for {}", func_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
#[cfg(not(feature = "cranelift-jit"))]
|
||||
{
|
||||
// Without Cranelift, do not register a stub that alters program semantics.
|
||||
// Report as not compiled so VM path remains authoritative.
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
|
||||
let dt = t0.elapsed();
|
||||
eprintln!(
|
||||
"[JIT] compile skipped (no cranelift) for {} after {}ms",
|
||||
func_name,
|
||||
dt.as_millis()
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get statistics from the last lowered function
|
||||
pub fn last_lower_stats(&self) -> (u64, u64, bool) {
|
||||
(
|
||||
self.last_phi_total,
|
||||
self.last_phi_b1,
|
||||
self.last_ret_bool_hint,
|
||||
)
|
||||
}
|
||||
|
||||
/// Execute compiled function by handle with trap fallback.
|
||||
/// Returns Some(VMValue) if executed successfully; None on missing handle or trap (panic).
|
||||
pub fn execute_handle(
|
||||
&self,
|
||||
handle: u64,
|
||||
args: &[crate::jit::abi::JitValue],
|
||||
) -> Option<crate::jit::abi::JitValue> {
|
||||
let f = match self.fntab.get(&handle) {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
};
|
||||
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (f)(args)));
|
||||
match res {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => {
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[JIT] trap: panic during handle={} execution — falling back to VM",
|
||||
handle
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Register built-in externs (collections)
|
||||
fn register_default_externs(&mut self) {
|
||||
use crate::jit::r#extern::collections as c;
|
||||
use crate::jit::r#extern::host_bridge as hb;
|
||||
self.register_extern(c::SYM_ARRAY_LEN, Arc::new(|args| c::array_len(args)));
|
||||
self.register_extern(c::SYM_ARRAY_GET, Arc::new(|args| c::array_get(args)));
|
||||
self.register_extern(c::SYM_ARRAY_SET, Arc::new(|args| c::array_set(args)));
|
||||
self.register_extern(c::SYM_ARRAY_PUSH, Arc::new(|args| c::array_push(args)));
|
||||
self.register_extern(c::SYM_MAP_GET, Arc::new(|args| c::map_get(args)));
|
||||
self.register_extern(c::SYM_MAP_SET, Arc::new(|args| c::map_set(args)));
|
||||
self.register_extern(c::SYM_MAP_SIZE, Arc::new(|args| c::map_size(args)));
|
||||
// Host-bridge variants (by-slot via C symbol). Guarded by env opt-in for now.
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
self.register_extern(hb::SYM_HOST_ARRAY_LEN, Arc::new(|args| hb::array_len(args)));
|
||||
self.register_extern(hb::SYM_HOST_ARRAY_GET, Arc::new(|args| hb::array_get(args)));
|
||||
self.register_extern(hb::SYM_HOST_ARRAY_SET, Arc::new(|args| hb::array_set(args)));
|
||||
self.register_extern(hb::SYM_HOST_MAP_SIZE, Arc::new(|args| hb::map_size(args)));
|
||||
self.register_extern(hb::SYM_HOST_MAP_GET, Arc::new(|args| hb::map_get(args)));
|
||||
self.register_extern(hb::SYM_HOST_MAP_SET, Arc::new(|args| hb::map_set(args)));
|
||||
self.register_extern(hb::SYM_HOST_MAP_HAS, Arc::new(|args| hb::map_has(args)));
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_CONSOLE_LOG,
|
||||
Arc::new(|args| hb::console_log(args)),
|
||||
);
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_CONSOLE_WARN,
|
||||
Arc::new(|args| hb::console_warn(args)),
|
||||
);
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_CONSOLE_ERROR,
|
||||
Arc::new(|args| hb::console_error(args)),
|
||||
);
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_INSTANCE_GETFIELD,
|
||||
Arc::new(|args| hb::instance_getfield(args)),
|
||||
);
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_INSTANCE_SETFIELD,
|
||||
Arc::new(|args| hb::instance_setfield(args)),
|
||||
);
|
||||
self.register_extern(
|
||||
hb::SYM_HOST_STRING_LEN,
|
||||
Arc::new(|args| hb::string_len(args)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_extern(
|
||||
&mut self,
|
||||
name: &str,
|
||||
f: Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>,
|
||||
) {
|
||||
self.externs.insert(name.to_string(), f);
|
||||
}
|
||||
|
||||
/// Lookup an extern symbol (to be used by the lowering once call emission is added)
|
||||
pub fn lookup_extern(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Option<
|
||||
Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>,
|
||||
> {
|
||||
self.externs.get(name).cloned()
|
||||
}
|
||||
}
|
||||
138
archive/jit-cranelift/src/jit/events.rs
Normal file
138
archive/jit-cranelift/src/jit/events.rs
Normal file
@ -0,0 +1,138 @@
|
||||
//! JIT Events (v0): minimal JSONL appender for compile/execute/fallback/trap
|
||||
//!
|
||||
//! Emission is opt-in via env:
|
||||
//! - NYASH_JIT_EVENTS=1 prints to stdout (one JSON per line)
|
||||
//! - NYASH_JIT_EVENTS_PATH=/path/to/file.jsonl appends to file
|
||||
|
||||
use serde::Serialize;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
// Compile-phase counters (process-local)
|
||||
static LOWER_FALLBACK_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Reset compile-phase counters (call at the beginning of each lower/compile)
|
||||
pub fn lower_counters_reset() {
|
||||
LOWER_FALLBACK_COUNT.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get number of fallback decisions observed during lowering
|
||||
pub fn lower_fallbacks_get() -> u64 {
|
||||
LOWER_FALLBACK_COUNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn record_lower_decision(extra: &serde_json::Value) {
|
||||
// We record even when emission is disabled, to allow strict-mode checks.
|
||||
if let serde_json::Value::Object(map) = extra {
|
||||
if let Some(serde_json::Value::String(dec)) = map.get("decision") {
|
||||
if dec == "fallback" {
|
||||
LOWER_FALLBACK_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn base_emit_enabled() -> bool {
|
||||
std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_JIT_EVENTS_PATH").is_ok()
|
||||
}
|
||||
|
||||
fn should_emit_lower() -> bool {
|
||||
// Unify observability: if base events are on (stdout/file) or explicit compile flag, emit.
|
||||
base_emit_enabled() || std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
fn should_emit_runtime() -> bool {
|
||||
base_emit_enabled() || std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
fn write_line(s: &str) {
|
||||
if let Ok(path) = std::env::var("NYASH_JIT_EVENTS_PATH") {
|
||||
let _ = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)
|
||||
.and_then(|mut f| {
|
||||
use std::io::Write;
|
||||
writeln!(f, "{}", s)
|
||||
});
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Event<'a, T: Serialize> {
|
||||
kind: &'a str,
|
||||
function: &'a str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
handle: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ms: Option<u128>,
|
||||
#[serde(flatten)]
|
||||
extra: T,
|
||||
}
|
||||
|
||||
pub fn emit<T: Serialize>(
|
||||
kind: &str,
|
||||
function: &str,
|
||||
handle: Option<u64>,
|
||||
ms: Option<u128>,
|
||||
extra: T,
|
||||
) {
|
||||
if !base_emit_enabled() {
|
||||
return;
|
||||
}
|
||||
let ev = Event {
|
||||
kind,
|
||||
function,
|
||||
handle,
|
||||
ms,
|
||||
extra,
|
||||
};
|
||||
if let Ok(s) = serde_json::to_string(&ev) {
|
||||
write_line(&s);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_any(
|
||||
kind: &str,
|
||||
function: &str,
|
||||
handle: Option<u64>,
|
||||
ms: Option<u128>,
|
||||
extra: serde_json::Value,
|
||||
) {
|
||||
let ev = Event {
|
||||
kind,
|
||||
function,
|
||||
handle,
|
||||
ms,
|
||||
extra,
|
||||
};
|
||||
if let Ok(s) = serde_json::to_string(&ev) {
|
||||
write_line(&s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit an event during lowering (compile-time planning). Adds phase="lower".
|
||||
pub fn emit_lower(mut extra: serde_json::Value, kind: &str, function: &str) {
|
||||
// Always record decisions for strict-mode enforcement
|
||||
record_lower_decision(&extra);
|
||||
if !should_emit_lower() {
|
||||
return;
|
||||
}
|
||||
if let serde_json::Value::Object(ref mut map) = extra {
|
||||
map.insert("phase".into(), serde_json::Value::String("lower".into()));
|
||||
}
|
||||
emit_any(kind, function, None, None, extra);
|
||||
}
|
||||
|
||||
/// Emit an event during runtime execution. Adds phase="execute".
|
||||
pub fn emit_runtime(mut extra: serde_json::Value, kind: &str, function: &str) {
|
||||
if !should_emit_runtime() {
|
||||
return;
|
||||
}
|
||||
if let serde_json::Value::Object(ref mut map) = extra {
|
||||
map.insert("phase".into(), serde_json::Value::String("execute".into()));
|
||||
}
|
||||
emit_any(kind, function, None, None, extra);
|
||||
}
|
||||
66
archive/jit-cranelift/src/jit/extern/async.rs
vendored
Normal file
66
archive/jit-cranelift/src/jit/extern/async.rs
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
//! Async/Future-related JIT extern symbols
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::{
|
||||
backend::vm::VMValue,
|
||||
box_trait::{BoolBox, IntegerBox, NyashBox, StringBox},
|
||||
};
|
||||
|
||||
/// Symbol name for awaiting a FutureBox and returning a value/handle (i64)
|
||||
pub const SYM_FUTURE_AWAIT_H: &str = "nyash.future.await_h";
|
||||
pub const SYM_FUTURE_SPAWN_INSTANCE3_I64: &str = "nyash.future.spawn_instance3_i64";
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 {
|
||||
use crate::jit::rt::handles;
|
||||
|
||||
// Resolve FutureBox from handle or legacy VM args
|
||||
let mut fut_opt: Option<crate::boxes::future::FutureBox> = None;
|
||||
if arg0 > 0 {
|
||||
if let Some(obj) = handles::get(arg0 as u64) {
|
||||
if let Some(fb) = obj
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::FutureBox>()
|
||||
{
|
||||
fut_opt = Some(fb.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
if fut_opt.is_none() {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let pick = if arg0 >= 0 {
|
||||
(arg0 as usize)..(arg0 as usize + 1)
|
||||
} else {
|
||||
0..args.len()
|
||||
};
|
||||
for i in pick {
|
||||
if let Some(VMValue::BoxRef(b)) = args.get(i) {
|
||||
if let Some(fb) = b.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
fut_opt = Some(fb.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
let Some(fut) = fut_opt else {
|
||||
return 0;
|
||||
};
|
||||
// Cooperative wait with scheduler polling and timeout
|
||||
let max_ms: u64 = crate::config::env::await_max_ms();
|
||||
let start = std::time::Instant::now();
|
||||
while !fut.ready() {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
// Timeout: return 0 (caller may handle as failure)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Get NyashBox result and always return a handle
|
||||
let out_box: Box<dyn NyashBox> = fut.get();
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(out_box);
|
||||
let h = handles::to_handle(arc);
|
||||
h as i64
|
||||
}
|
||||
5
archive/jit-cranelift/src/jit/extern/birth.rs
vendored
Normal file
5
archive/jit-cranelift/src/jit/extern/birth.rs
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
//! Generic birth hostcall symbol names
|
||||
|
||||
pub const SYM_BOX_BIRTH_H: &str = "nyash.box.birth_h";
|
||||
/// Birth by type name encoded in two u64 words (lo,hi,len)
|
||||
pub const SYM_INSTANCE_BIRTH_NAME_U64X2: &str = "nyash.instance.birth_name_u64x2";
|
||||
182
archive/jit-cranelift/src/jit/extern/collections.rs
vendored
Normal file
182
archive/jit-cranelift/src/jit/extern/collections.rs
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::backend::vm::VMValue;
|
||||
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
|
||||
|
||||
/// Symbol names for host externs (stable ABI for JIT)
|
||||
pub const SYM_ARRAY_LEN: &str = "nyash.array.len";
|
||||
pub const SYM_ARRAY_GET: &str = "nyash.array.get";
|
||||
pub const SYM_ARRAY_SET: &str = "nyash.array.set";
|
||||
pub const SYM_ARRAY_PUSH: &str = "nyash.array.push";
|
||||
|
||||
pub const SYM_MAP_GET: &str = "nyash.map.get";
|
||||
pub const SYM_MAP_SET: &str = "nyash.map.set";
|
||||
pub const SYM_MAP_SIZE: &str = "nyash.map.size";
|
||||
|
||||
// Handle-based variants for direct JIT bridging
|
||||
pub const SYM_ARRAY_LEN_H: &str = "nyash.array.len_h";
|
||||
pub const SYM_ARRAY_GET_H: &str = "nyash.array.get_h";
|
||||
pub const SYM_ARRAY_SET_H: &str = "nyash.array.set_h";
|
||||
pub const SYM_ARRAY_SET_HH: &str = "nyash.array.set_hh";
|
||||
pub const SYM_ARRAY_PUSH_H: &str = "nyash.array.push_h";
|
||||
pub const SYM_ARRAY_LAST_H: &str = "nyash.array.last_h";
|
||||
pub const SYM_MAP_SIZE_H: &str = "nyash.map.size_h";
|
||||
pub const SYM_MAP_GET_H: &str = "nyash.map.get_h";
|
||||
pub const SYM_MAP_GET_HH: &str = "nyash.map.get_hh";
|
||||
pub const SYM_MAP_SET_H: &str = "nyash.map.set_h";
|
||||
pub const SYM_MAP_HAS_H: &str = "nyash.map.has_h";
|
||||
// Generic read-only helper
|
||||
pub const SYM_ANY_LEN_H: &str = "nyash.any.length_h";
|
||||
pub const SYM_ANY_IS_EMPTY_H: &str = "nyash.any.is_empty_h";
|
||||
pub const SYM_STRING_CHARCODE_AT_H: &str = "nyash.string.charCodeAt_h";
|
||||
pub const SYM_STRING_LEN_H: &str = "nyash.string.len_h";
|
||||
pub const SYM_STRING_BIRTH_H: &str = "nyash.string.birth_h";
|
||||
pub const SYM_STRING_FROM_U64X2: &str = "nyash.string.from_u64x2";
|
||||
pub const SYM_INTEGER_BIRTH_H: &str = "nyash.integer.birth_h";
|
||||
pub const SYM_CONSOLE_BIRTH_H: &str = "nyash.console.birth_h";
|
||||
// String-like operations (handle, handle)
|
||||
pub const SYM_STRING_CONCAT_HH: &str = "nyash.string.concat_hh";
|
||||
pub const SYM_STRING_EQ_HH: &str = "nyash.string.eq_hh";
|
||||
pub const SYM_STRING_LT_HH: &str = "nyash.string.lt_hh";
|
||||
// Unified semantics: addition for dynamic boxes (handle,handle)
|
||||
pub const SYM_SEMANTICS_ADD_HH: &str = "nyash.semantics.add_hh";
|
||||
|
||||
fn as_array(args: &[VMValue]) -> Option<&crate::boxes::array::ArrayBox> {
|
||||
match args.get(0) {
|
||||
Some(VMValue::BoxRef(b)) => b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_map(args: &[VMValue]) -> Option<&crate::boxes::map_box::MapBox> {
|
||||
match args.get(0) {
|
||||
Some(VMValue::BoxRef(b)) => b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn array_len(args: &[VMValue]) -> VMValue {
|
||||
if let Some(arr) = as_array(args) {
|
||||
if let Some(len_box) = arr.length().as_any().downcast_ref::<IntegerBox>() {
|
||||
return VMValue::Integer(len_box.value);
|
||||
}
|
||||
}
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
|
||||
pub fn array_get(args: &[VMValue]) -> VMValue {
|
||||
if let (Some(arr), Some(VMValue::Integer(idx))) = (as_array(args), args.get(1)) {
|
||||
// ArrayBox.get expects a NyashBox index
|
||||
let val = arr.get(Box::new(IntegerBox::new(*idx)));
|
||||
return VMValue::from_nyash_box(val);
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn array_set(args: &[VMValue]) -> VMValue {
|
||||
// Enforce policy for mutating operation
|
||||
if crate::jit::policy::current().read_only
|
||||
&& !crate::jit::policy::current()
|
||||
.hostcall_whitelist
|
||||
.iter()
|
||||
.any(|s| s == SYM_ARRAY_SET)
|
||||
{
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_ARRAY_SET, "decision":"fallback", "reason":"policy_denied_mutating"}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::Integer(0);
|
||||
}
|
||||
if let (Some(arr), Some(VMValue::Integer(idx)), Some(value)) =
|
||||
(as_array(args), args.get(1), args.get(2))
|
||||
{
|
||||
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
|
||||
let res = arr.set(Box::new(IntegerBox::new(*idx)), val_box);
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_ARRAY_SET, "decision":"allow", "argc":3, "arg_types":["Handle","I64","Handle"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::from_nyash_box(res);
|
||||
}
|
||||
VMValue::BoxRef(Arc::new(StringBox::new(
|
||||
"Error: array.set expects (ArrayBox, i64, value)",
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn array_push(args: &[VMValue]) -> VMValue {
|
||||
if crate::jit::policy::current().read_only
|
||||
&& !crate::jit::policy::current()
|
||||
.hostcall_whitelist
|
||||
.iter()
|
||||
.any(|s| s == SYM_ARRAY_PUSH)
|
||||
{
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_ARRAY_PUSH, "decision":"fallback", "reason":"policy_denied_mutating"}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::Integer(0);
|
||||
}
|
||||
if let (Some(arr), Some(value)) = (as_array(args), args.get(1)) {
|
||||
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
|
||||
let res = arr.push(val_box);
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_ARRAY_PUSH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::from_nyash_box(res);
|
||||
}
|
||||
VMValue::BoxRef(Arc::new(StringBox::new(
|
||||
"Error: array.push expects (ArrayBox, value)",
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn map_get(args: &[VMValue]) -> VMValue {
|
||||
if let (Some(map), Some(key)) = (as_map(args), args.get(1)) {
|
||||
let key_box: Box<dyn NyashBox> = key.to_nyash_box();
|
||||
return VMValue::from_nyash_box(map.get(key_box));
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn map_set(args: &[VMValue]) -> VMValue {
|
||||
if crate::jit::policy::current().read_only
|
||||
&& !crate::jit::policy::current()
|
||||
.hostcall_whitelist
|
||||
.iter()
|
||||
.any(|s| s == SYM_MAP_SET)
|
||||
{
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_MAP_SET, "decision":"fallback", "reason":"policy_denied_mutating"}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::Integer(0);
|
||||
}
|
||||
if let (Some(map), Some(key), Some(value)) = (as_map(args), args.get(1), args.get(2)) {
|
||||
let key_box: Box<dyn NyashBox> = key.to_nyash_box();
|
||||
let val_box: Box<dyn NyashBox> = value.to_nyash_box();
|
||||
let out = map.set(key_box, val_box);
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({"id": SYM_MAP_SET, "decision":"allow", "argc":3, "arg_types":["Handle","Handle","Handle"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
return VMValue::from_nyash_box(out);
|
||||
}
|
||||
VMValue::BoxRef(Arc::new(StringBox::new(
|
||||
"Error: map.set expects (MapBox, key, value)",
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn map_size(args: &[VMValue]) -> VMValue {
|
||||
if let Some(map) = as_map(args) {
|
||||
if let Some(sz) = map.size().as_any().downcast_ref::<IntegerBox>() {
|
||||
return VMValue::Integer(sz.value);
|
||||
}
|
||||
}
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
3
archive/jit-cranelift/src/jit/extern/handles.rs
vendored
Normal file
3
archive/jit-cranelift/src/jit/extern/handles.rs
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
//! Handle-related extern symbol names
|
||||
|
||||
pub const SYM_HANDLE_OF: &str = "nyash.handle.of";
|
||||
222
archive/jit-cranelift/src/jit/extern/host_bridge.rs
vendored
Normal file
222
archive/jit-cranelift/src/jit/extern/host_bridge.rs
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
#![allow(unused_unsafe)]
|
||||
//! JIT externs bridging to NyRT host API (C symbols) via by-slot encoding.
|
||||
//!
|
||||
//! 目的: VM/JIT一致のため、JITからも host_api::nyrt_host_call_slot を使うPoC。
|
||||
|
||||
use crate::backend::vm::VMValue;
|
||||
|
||||
fn tlv_encode_values(args: &[VMValue]) -> Vec<u8> {
|
||||
use crate::runtime::plugin_ffi_common::encode as enc;
|
||||
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
|
||||
for a in args {
|
||||
match a {
|
||||
VMValue::Integer(i) => enc::i64(&mut buf, *i),
|
||||
VMValue::Float(f) => enc::f64(&mut buf, *f),
|
||||
VMValue::Bool(b) => enc::bool(&mut buf, *b),
|
||||
VMValue::String(s) => enc::string(&mut buf, s),
|
||||
VMValue::BoxRef(arc) => {
|
||||
// Try to downcast common primitives for stable TLV
|
||||
if let Some(sb) = arc.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
enc::string(&mut buf, &sb.value);
|
||||
} else if let Some(ib) = arc.as_any().downcast_ref::<crate::box_trait::IntegerBox>()
|
||||
{
|
||||
enc::i64(&mut buf, ib.value);
|
||||
} else if let Some(bb) = arc.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
enc::bool(&mut buf, bb.value);
|
||||
} else if let Some(fb) = arc
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::math_box::FloatBox>()
|
||||
{
|
||||
enc::f64(&mut buf, fb.value);
|
||||
} else {
|
||||
// Fallback: send HostHandle so host can operate on it if needed
|
||||
let h = crate::runtime::host_handles::to_handle_arc(arc.clone());
|
||||
enc::host_handle(&mut buf, h);
|
||||
}
|
||||
}
|
||||
VMValue::Future(fu) => {
|
||||
let bx: Box<dyn crate::box_trait::NyashBox> = Box::new(fu.clone());
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(bx);
|
||||
let h = crate::runtime::host_handles::to_handle_arc(arc);
|
||||
enc::host_handle(&mut buf, h);
|
||||
}
|
||||
VMValue::Void => enc::string(&mut buf, "void"),
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
fn call_slot(handle: u64, slot: u64, argv: &[VMValue]) -> VMValue {
|
||||
let tlv = tlv_encode_values(argv);
|
||||
let mut out = vec![0u8; 256];
|
||||
let mut out_len: usize = out.len();
|
||||
let code = unsafe {
|
||||
crate::runtime::host_api::nyrt_host_call_slot(
|
||||
handle,
|
||||
slot,
|
||||
tlv.as_ptr(),
|
||||
tlv.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
)
|
||||
};
|
||||
if code != 0 {
|
||||
return VMValue::Void;
|
||||
}
|
||||
if let Some((tag, _sz, payload)) =
|
||||
crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||
{
|
||||
match tag {
|
||||
6 | 7 => {
|
||||
let s = crate::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let sb = crate::box_trait::StringBox::new(&s);
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(sb);
|
||||
VMValue::BoxRef(arc)
|
||||
}
|
||||
1 => crate::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.map(VMValue::Bool)
|
||||
.unwrap_or(VMValue::Void),
|
||||
2 => crate::runtime::plugin_ffi_common::decode::i32(payload)
|
||||
.map(|v| VMValue::Integer(v as i64))
|
||||
.unwrap_or(VMValue::Void),
|
||||
3 => crate::runtime::plugin_ffi_common::decode::u64(payload)
|
||||
.map(|v| VMValue::Integer(v as i64))
|
||||
.unwrap_or(VMValue::Void),
|
||||
5 => crate::runtime::plugin_ffi_common::decode::f64(payload)
|
||||
.map(VMValue::Float)
|
||||
.unwrap_or(VMValue::Void),
|
||||
9 => {
|
||||
if let Some(h) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||
if let Some(arc) = crate::runtime::host_handles::get(h) {
|
||||
return VMValue::BoxRef(arc);
|
||||
}
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
_ => VMValue::Void,
|
||||
}
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
|
||||
fn to_handle(recv: &VMValue) -> Option<u64> {
|
||||
match recv {
|
||||
VMValue::BoxRef(arc) => Some(crate::runtime::host_handles::to_handle_arc(arc.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Public bridge helpers (symbol strings align with collections for PoC)
|
||||
pub const SYM_HOST_ARRAY_GET: &str = "nyash.host.array.get"; // (ArrayBox, i64)
|
||||
pub const SYM_HOST_ARRAY_SET: &str = "nyash.host.array.set"; // (ArrayBox, i64, val)
|
||||
pub const SYM_HOST_ARRAY_LEN: &str = "nyash.host.array.len"; // (ArrayBox)
|
||||
pub const SYM_HOST_MAP_GET: &str = "nyash.host.map.get"; // (MapBox, key)
|
||||
pub const SYM_HOST_MAP_SET: &str = "nyash.host.map.set"; // (MapBox, key, val)
|
||||
pub const SYM_HOST_MAP_SIZE: &str = "nyash.host.map.size"; // (MapBox)
|
||||
pub const SYM_HOST_MAP_HAS: &str = "nyash.host.map.has"; // (MapBox, key)
|
||||
pub const SYM_HOST_CONSOLE_LOG: &str = "nyash.host.console.log"; // (value)
|
||||
pub const SYM_HOST_CONSOLE_WARN: &str = "nyash.host.console.warn"; // (value)
|
||||
pub const SYM_HOST_CONSOLE_ERROR: &str = "nyash.host.console.error"; // (value)
|
||||
pub const SYM_HOST_INSTANCE_GETFIELD: &str = "nyash.host.instance.getField"; // (InstanceBox, name)
|
||||
pub const SYM_HOST_INSTANCE_SETFIELD: &str = "nyash.host.instance.setField"; // (InstanceBox, name, value)
|
||||
// Arity-stable variants for Cranelift imports (avoid signature conflicts)
|
||||
pub const SYM_HOST_INSTANCE_GETFIELD2: &str = "nyash.host.instance.getField2"; // (InstanceBox, name)
|
||||
pub const SYM_HOST_INSTANCE_SETFIELD3: &str = "nyash.host.instance.setField3"; // (InstanceBox, name, value)
|
||||
pub const SYM_HOST_INSTANCE_FIELD3: &str = "nyash.host.instance.field3"; // (recv,name,val or sentinel)
|
||||
pub const SYM_HOST_STRING_LEN: &str = "nyash.host.string.len"; // (StringBox)
|
||||
|
||||
pub fn array_get(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 100, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
pub fn array_set(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 101, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
pub fn array_len(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 102, &[])
|
||||
} else {
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
}
|
||||
pub fn map_get(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 203, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
pub fn map_set(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 204, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
pub fn map_size(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 200, &[])
|
||||
} else {
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
}
|
||||
pub fn map_has(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 202, &args[1..])
|
||||
} else {
|
||||
VMValue::Bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn console_log(args: &[VMValue]) -> VMValue {
|
||||
// JIT host-bridge簡易版: 最初の引数を文字列化してstdoutへ
|
||||
if let Some(a0) = args.get(0) {
|
||||
println!("{}", a0.to_string());
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn console_warn(args: &[VMValue]) -> VMValue {
|
||||
if let Some(a0) = args.get(0) {
|
||||
eprintln!("[warn] {}", a0.to_string());
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn console_error(args: &[VMValue]) -> VMValue {
|
||||
if let Some(a0) = args.get(0) {
|
||||
eprintln!("[error] {}", a0.to_string());
|
||||
}
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn instance_getfield(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 1, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
pub fn instance_setfield(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 2, &args[1..])
|
||||
} else {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_len(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) {
|
||||
call_slot(h, 300, &[])
|
||||
} else {
|
||||
VMValue::Integer(0)
|
||||
}
|
||||
}
|
||||
13
archive/jit-cranelift/src/jit/extern/mod.rs
vendored
Normal file
13
archive/jit-cranelift/src/jit/extern/mod.rs
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//! Host-callable externs for JIT-compiled code
|
||||
//!
|
||||
//! Phase 10_d: Provide thin bridges for Array/Map hot operations that
|
||||
//! JIT can call via symbol names. Lowering will resolve MIR ops into
|
||||
//! these externs once call emission is added.
|
||||
|
||||
pub mod r#async;
|
||||
pub mod birth;
|
||||
pub mod collections;
|
||||
pub mod handles;
|
||||
pub mod host_bridge;
|
||||
pub mod result;
|
||||
pub mod runtime;
|
||||
44
archive/jit-cranelift/src/jit/extern/result.rs
vendored
Normal file
44
archive/jit-cranelift/src/jit/extern/result.rs
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
//! Result-related JIT extern symbols
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// Symbol name for wrapping a handle into Result.Ok(handle)
|
||||
pub const SYM_RESULT_OK_H: &str = "nyash.result.ok_h";
|
||||
/// Symbol name for wrapping a handle into Result.Err(handle)
|
||||
pub const SYM_RESULT_ERR_H: &str = "nyash.result.err_h";
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_result_ok_h(handle: i64) -> i64 {
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
use crate::jit::rt::handles;
|
||||
if handle <= 0 {
|
||||
return 0;
|
||||
}
|
||||
if let Some(obj) = handles::get(handle as u64) {
|
||||
let boxed = obj.clone_box();
|
||||
let res = NyashResultBox::new_ok(boxed);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(res);
|
||||
let h = handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_result_err_h(handle: i64) -> i64 {
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
use crate::jit::rt::handles;
|
||||
// If handle <= 0, synthesize a Timeout StringBox error for await paths.
|
||||
let err_box: Box<dyn NyashBox> = if handle <= 0 {
|
||||
Box::new(crate::box_trait::StringBox::new("Timeout".to_string()))
|
||||
} else if let Some(obj) = handles::get(handle as u64) {
|
||||
obj.clone_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::StringBox::new("UnknownError".to_string()))
|
||||
};
|
||||
let res = NyashResultBox::new_err(err_box);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(res);
|
||||
let h = handles::to_handle(arc);
|
||||
h as i64
|
||||
}
|
||||
7
archive/jit-cranelift/src/jit/extern/runtime.rs
vendored
Normal file
7
archive/jit-cranelift/src/jit/extern/runtime.rs
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//! Runtime/GC related hostcall symbol names reserved for JIT/AOT.
|
||||
|
||||
/// Runtime safepoint checkpoint (no-op stub for now)
|
||||
pub const SYM_RT_CHECKPOINT: &str = "nyash.rt.checkpoint";
|
||||
|
||||
/// Write barrier hint for GC (no-op stub for now)
|
||||
pub const SYM_GC_BARRIER_WRITE: &str = "nyash.gc.barrier_write";
|
||||
357
archive/jit-cranelift/src/jit/hostcall_registry.rs
Normal file
357
archive/jit-cranelift/src/jit/hostcall_registry.rs
Normal file
@ -0,0 +1,357 @@
|
||||
//! Minimal hostcall registry (v0): classify symbols as read-only or mutating
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HostcallKind {
|
||||
ReadOnly,
|
||||
Mutating,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Registry {
|
||||
ro: HashSet<String>,
|
||||
mu: HashSet<String>,
|
||||
// Allow multiple signatures per symbol (overloads)
|
||||
sig: HashMap<String, Vec<Signature>>,
|
||||
}
|
||||
|
||||
static REG: OnceCell<RwLock<Registry>> = OnceCell::new();
|
||||
|
||||
fn ensure_default() {
|
||||
if REG.get().is_some() {
|
||||
return;
|
||||
}
|
||||
let mut r = Registry::default();
|
||||
// Read-only defaults
|
||||
for s in [
|
||||
"nyash.array.len_h",
|
||||
"nyash.string.len_h",
|
||||
"nyash.any.length_h",
|
||||
"nyash.any.is_empty_h",
|
||||
"nyash.map.size_h",
|
||||
"nyash.map.get_h",
|
||||
"nyash.map.has_h",
|
||||
"nyash.string.charCodeAt_h",
|
||||
"nyash.string.concat_hh",
|
||||
"nyash.string.eq_hh",
|
||||
"nyash.string.lt_hh",
|
||||
"nyash.array.get_h",
|
||||
] {
|
||||
r.ro.insert(s.to_string());
|
||||
}
|
||||
// Mutating defaults
|
||||
for s in ["nyash.array.push_h", "nyash.array.set_h", "nyash.map.set_h"] {
|
||||
r.mu.insert(s.to_string());
|
||||
}
|
||||
// Signatures (v0): register known symbols with simple arg/ret kinds
|
||||
// math.* thin bridge: f64 signatures only (allow when args match exactly)
|
||||
r.sig
|
||||
.entry("nyash.math.sin".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::F64],
|
||||
ret: ArgKind::F64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.math.cos".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::F64],
|
||||
ret: ArgKind::F64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.math.abs".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::F64],
|
||||
ret: ArgKind::F64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.math.min".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::F64, ArgKind::F64],
|
||||
ret: ArgKind::F64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.math.max".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::F64, ArgKind::F64],
|
||||
ret: ArgKind::F64,
|
||||
});
|
||||
// Collections (handle-based)
|
||||
// Map get: support both integer and handle keys (overload)
|
||||
r.sig
|
||||
.entry("nyash.map.get_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::I64],
|
||||
ret: ArgKind::Handle,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.map.get_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::Handle],
|
||||
ret: ArgKind::Handle,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.map.size_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.array.get_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::I64],
|
||||
ret: ArgKind::Handle,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.array.len_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
// String helpers
|
||||
r.sig
|
||||
.entry("nyash.string.len_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.string.charCodeAt_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::I64],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.string.concat_hh".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::Handle],
|
||||
ret: ArgKind::Handle,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.semantics.add_hh".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::Handle],
|
||||
ret: ArgKind::Handle,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.string.eq_hh".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.string.lt_hh".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
// Any helpers (length/is_empty)
|
||||
r.sig
|
||||
.entry("nyash.any.length_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
r.sig
|
||||
.entry("nyash.any.is_empty_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
// Map.has(handle, i64) -> i64(0/1)
|
||||
r.sig
|
||||
.entry("nyash.map.has_h".to_string())
|
||||
.or_default()
|
||||
.push(Signature {
|
||||
args: vec![ArgKind::Handle, ArgKind::I64],
|
||||
ret: ArgKind::I64,
|
||||
});
|
||||
let _ = REG.set(RwLock::new(r));
|
||||
}
|
||||
|
||||
pub fn classify(symbol: &str) -> HostcallKind {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(g) = lock.read() {
|
||||
if g.ro.contains(symbol) {
|
||||
return HostcallKind::ReadOnly;
|
||||
}
|
||||
if g.mu.contains(symbol) {
|
||||
return HostcallKind::Mutating;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Default to read-only to be permissive in v0
|
||||
HostcallKind::ReadOnly
|
||||
}
|
||||
|
||||
pub fn add_readonly(symbol: &str) {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(mut w) = lock.write() {
|
||||
w.ro.insert(symbol.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn add_mutating(symbol: &str) {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(mut w) = lock.write() {
|
||||
w.mu.insert(symbol.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn set_from_csv(ro_csv: &str, mu_csv: &str) {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(mut w) = lock.write() {
|
||||
w.ro.clear();
|
||||
w.mu.clear();
|
||||
for s in ro_csv.split(',') {
|
||||
let t = s.trim();
|
||||
if !t.is_empty() {
|
||||
w.ro.insert(t.to_string());
|
||||
}
|
||||
}
|
||||
for s in mu_csv.split(',') {
|
||||
let t = s.trim();
|
||||
if !t.is_empty() {
|
||||
w.mu.insert(t.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn snapshot() -> (Vec<String>, Vec<String>) {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(g) = lock.read() {
|
||||
let mut ro: Vec<String> = g.ro.iter().cloned().collect();
|
||||
ro.sort();
|
||||
let mut mu: Vec<String> = g.mu.iter().cloned().collect();
|
||||
mu.sort();
|
||||
return (ro, mu);
|
||||
}
|
||||
}
|
||||
(Vec::new(), Vec::new())
|
||||
}
|
||||
|
||||
// ==== Signature (v0 scaffolding) ====
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ArgKind {
|
||||
I64,
|
||||
F64,
|
||||
Handle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Signature {
|
||||
pub args: Vec<ArgKind>,
|
||||
pub ret: ArgKind,
|
||||
}
|
||||
|
||||
fn parse_kind(s: &str) -> Option<ArgKind> {
|
||||
match s.trim().to_ascii_lowercase().as_str() {
|
||||
"i64" | "int" | "integer" => Some(ArgKind::I64),
|
||||
"f64" | "float" => Some(ArgKind::F64),
|
||||
"handle" | "h" => Some(ArgKind::Handle),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_signature_csv(symbol: &str, args_csv: &str, ret_str: &str) -> bool {
|
||||
ensure_default();
|
||||
let mut ok = true;
|
||||
let parsed: Vec<Option<ArgKind>> = args_csv
|
||||
.split(',')
|
||||
.filter(|t| !t.trim().is_empty())
|
||||
.map(|t| parse_kind(t))
|
||||
.collect();
|
||||
let mut args: Vec<ArgKind> = Vec::new();
|
||||
for p in parsed {
|
||||
if let Some(k) = p {
|
||||
args.push(k)
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
let ret = match parse_kind(ret_str) {
|
||||
Some(k) => k,
|
||||
None => {
|
||||
ok = false;
|
||||
ArgKind::I64
|
||||
}
|
||||
};
|
||||
if !ok {
|
||||
return false;
|
||||
}
|
||||
let sig = Signature { args, ret };
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(mut w) = lock.write() {
|
||||
w.sig.entry(symbol.to_string()).or_default().push(sig);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check observed args against a registered signature.
|
||||
/// - If no signature is registered for the symbol, returns Ok(()) to be permissive in v0.
|
||||
/// - Returns Err("sig_mismatch") when arg length or kinds differ.
|
||||
pub fn check_signature(symbol: &str, observed_args: &[ArgKind]) -> Result<(), &'static str> {
|
||||
ensure_default();
|
||||
if let Some(lock) = REG.get() {
|
||||
if let Ok(g) = lock.read() {
|
||||
if let Some(sigs) = g.sig.get(symbol) {
|
||||
let cfg_now = crate::jit::config::current();
|
||||
let relax = cfg_now.relax_numeric || cfg_now.native_f64;
|
||||
// Match against any one of the overload signatures
|
||||
'outer: for sig in sigs.iter() {
|
||||
if sig.args.len() != observed_args.len() {
|
||||
continue;
|
||||
}
|
||||
for (expected, observed) in sig.args.iter().zip(observed_args.iter()) {
|
||||
if expected == observed {
|
||||
continue;
|
||||
}
|
||||
// v0 coercion: allow I64 → F64 only when relaxed numeric is enabled
|
||||
if relax
|
||||
&& matches!(expected, ArgKind::F64)
|
||||
&& matches!(observed, ArgKind::I64)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Mismatch for this candidate signature
|
||||
continue 'outer;
|
||||
}
|
||||
// All args matched for this signature
|
||||
return Ok(());
|
||||
}
|
||||
// No overload matched
|
||||
return Err("sig_mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
120
archive/jit-cranelift/src/jit/lower/builder.rs
Normal file
120
archive/jit-cranelift/src/jit/lower/builder.rs
Normal file
@ -0,0 +1,120 @@
|
||||
//! IR builder abstraction (thin hub).
|
||||
//!
|
||||
//! Lowering targets the `IRBuilder` trait. Concrete backends live in
|
||||
//! submodules and are enabled via feature flags.
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BinOpKind {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CmpKind {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ParamKind {
|
||||
I64,
|
||||
F64,
|
||||
B1,
|
||||
}
|
||||
|
||||
pub trait IRBuilder {
|
||||
fn begin_function(&mut self, name: &str);
|
||||
fn end_function(&mut self);
|
||||
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {}
|
||||
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {}
|
||||
fn emit_param_i64(&mut self, _index: usize) {}
|
||||
fn emit_const_i64(&mut self, _val: i64);
|
||||
fn emit_const_f64(&mut self, _val: f64);
|
||||
fn emit_binop(&mut self, _op: BinOpKind);
|
||||
fn emit_compare(&mut self, _op: CmpKind);
|
||||
fn emit_jump(&mut self);
|
||||
fn emit_branch(&mut self);
|
||||
fn emit_return(&mut self);
|
||||
fn emit_select_i64(&mut self) {}
|
||||
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) {}
|
||||
fn emit_host_call_typed(
|
||||
&mut self,
|
||||
_symbol: &str,
|
||||
_params: &[ParamKind],
|
||||
_has_ret: bool,
|
||||
_ret_is_f64: bool,
|
||||
) {
|
||||
}
|
||||
fn emit_host_call_fixed3(&mut self, _symbol: &str, _has_ret: bool) {}
|
||||
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) {
|
||||
}
|
||||
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) {}
|
||||
// Create a StringBox handle from a string literal and push its handle (i64) onto the stack.
|
||||
fn emit_string_handle_from_literal(&mut self, _s: &str) {}
|
||||
fn prepare_blocks(&mut self, _count: usize) {}
|
||||
fn switch_to_block(&mut self, _index: usize) {}
|
||||
fn seal_block(&mut self, _index: usize) {}
|
||||
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) {}
|
||||
fn jump_to(&mut self, _target_index: usize) {}
|
||||
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) {}
|
||||
fn ensure_block_params_b1(&mut self, index: usize, count: usize) {
|
||||
self.ensure_block_params_i64(index, count);
|
||||
}
|
||||
fn ensure_block_param_i64(&mut self, index: usize) {
|
||||
self.ensure_block_params_i64(index, 1);
|
||||
}
|
||||
fn push_block_param_i64_at(&mut self, _pos: usize) {}
|
||||
fn push_block_param_b1_at(&mut self, _pos: usize) {
|
||||
self.push_block_param_i64_at(_pos);
|
||||
}
|
||||
fn push_block_param_i64(&mut self) {
|
||||
self.push_block_param_i64_at(0);
|
||||
}
|
||||
fn br_if_with_args(
|
||||
&mut self,
|
||||
_then_index: usize,
|
||||
_else_index: usize,
|
||||
_then_n: usize,
|
||||
_else_n: usize,
|
||||
) {
|
||||
self.br_if_top_is_true(_then_index, _else_index);
|
||||
}
|
||||
fn jump_with_args(&mut self, _target_index: usize, _n: usize) {
|
||||
self.jump_to(_target_index);
|
||||
}
|
||||
fn hint_ret_bool(&mut self, _is_b1: bool) {}
|
||||
fn ensure_local_i64(&mut self, _index: usize) {}
|
||||
fn store_local_i64(&mut self, _index: usize) {}
|
||||
fn load_local_i64(&mut self, _index: usize) {}
|
||||
// Optional debug hook: print a local i64 value with a tag (Cranelift JIT only)
|
||||
fn emit_debug_i64_local(&mut self, _tag: i64, _slot: usize) {}
|
||||
}
|
||||
|
||||
mod noop;
|
||||
pub use noop::NoopBuilder;
|
||||
|
||||
// Backend modules (feature-gated)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
mod cranelift;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub use cranelift::CraneliftBuilder;
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
mod object;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub use object::ObjectBuilder;
|
||||
|
||||
// TLS and runtime shim submodules used by Cranelift backend
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
mod tls;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(crate) use tls::clif_tls;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
mod rt_shims;
|
||||
1508
archive/jit-cranelift/src/jit/lower/builder/cranelift.rs
Normal file
1508
archive/jit-cranelift/src/jit/lower/builder/cranelift.rs
Normal file
File diff suppressed because it is too large
Load Diff
89
archive/jit-cranelift/src/jit/lower/builder/noop.rs
Normal file
89
archive/jit-cranelift/src/jit/lower/builder/noop.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use super::{BinOpKind, CmpKind, IRBuilder, ParamKind};
|
||||
|
||||
pub struct NoopBuilder {
|
||||
pub consts: usize,
|
||||
pub binops: usize,
|
||||
pub cmps: usize,
|
||||
pub branches: usize,
|
||||
pub rets: usize,
|
||||
}
|
||||
|
||||
impl NoopBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
consts: 0,
|
||||
binops: 0,
|
||||
cmps: 0,
|
||||
branches: 0,
|
||||
rets: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IRBuilder for NoopBuilder {
|
||||
fn begin_function(&mut self, _name: &str) {}
|
||||
fn end_function(&mut self) {}
|
||||
fn emit_param_i64(&mut self, _index: usize) {
|
||||
self.consts += 1;
|
||||
}
|
||||
fn emit_const_i64(&mut self, _val: i64) {
|
||||
self.consts += 1;
|
||||
}
|
||||
fn emit_const_f64(&mut self, _val: f64) {
|
||||
self.consts += 1;
|
||||
}
|
||||
fn emit_binop(&mut self, _op: BinOpKind) {
|
||||
self.binops += 1;
|
||||
}
|
||||
fn emit_compare(&mut self, _op: CmpKind) {
|
||||
self.cmps += 1;
|
||||
}
|
||||
fn emit_jump(&mut self) {
|
||||
self.branches += 1;
|
||||
}
|
||||
fn emit_branch(&mut self) {
|
||||
self.branches += 1;
|
||||
}
|
||||
fn emit_return(&mut self) {
|
||||
self.rets += 1;
|
||||
}
|
||||
fn emit_select_i64(&mut self) {
|
||||
self.binops += 1;
|
||||
}
|
||||
fn emit_host_call_typed(
|
||||
&mut self,
|
||||
_symbol: &str,
|
||||
_params: &[ParamKind],
|
||||
has_ret: bool,
|
||||
_ret_is_f64: bool,
|
||||
) {
|
||||
if has_ret {
|
||||
self.consts += 1;
|
||||
}
|
||||
}
|
||||
fn emit_host_call_fixed3(&mut self, _symbol: &str, has_ret: bool) {
|
||||
if has_ret {
|
||||
self.consts += 1;
|
||||
}
|
||||
}
|
||||
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, has_ret: bool) {
|
||||
if has_ret {
|
||||
self.consts += 1;
|
||||
}
|
||||
}
|
||||
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, has_ret: bool) {
|
||||
if has_ret {
|
||||
self.consts += 1;
|
||||
}
|
||||
}
|
||||
fn emit_string_handle_from_literal(&mut self, _s: &str) {
|
||||
self.consts += 1;
|
||||
}
|
||||
fn ensure_local_i64(&mut self, _index: usize) {}
|
||||
fn store_local_i64(&mut self, _index: usize) {
|
||||
self.consts += 1;
|
||||
}
|
||||
fn load_local_i64(&mut self, _index: usize) {
|
||||
self.consts += 1;
|
||||
}
|
||||
}
|
||||
1010
archive/jit-cranelift/src/jit/lower/builder/object.rs
Normal file
1010
archive/jit-cranelift/src/jit/lower/builder/object.rs
Normal file
File diff suppressed because it is too large
Load Diff
414
archive/jit-cranelift/src/jit/lower/builder/rt_shims.rs
Normal file
414
archive/jit-cranelift/src/jit/lower/builder/rt_shims.rs
Normal file
@ -0,0 +1,414 @@
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
// Runtime shims and helpers used by the Cranelift JIT backend
|
||||
|
||||
pub(crate) extern "C" fn nyash_host_stub0() -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
pub(crate) extern "C" fn nyash_jit_dbg_i64(tag: i64, val: i64) -> i64 {
|
||||
eprintln!("[JIT-DBG] tag={} val={}", tag, val);
|
||||
val
|
||||
}
|
||||
|
||||
pub(crate) extern "C" fn nyash_jit_block_enter(idx: i64) {
|
||||
eprintln!("[JIT-BLOCK] enter={}", idx);
|
||||
}
|
||||
|
||||
pub(crate) extern "C" fn nyash_plugin_invoke3_i64(
|
||||
type_id: i64,
|
||||
method_id: i64,
|
||||
argc: i64,
|
||||
a0: i64,
|
||||
a1: i64,
|
||||
a2: i64,
|
||||
) -> i64 {
|
||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
let trace = crate::jit::observe::trace_enabled();
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({ "id": "shim.enter.i64", "type_id": type_id, "method_id": method_id, "argc": argc }),
|
||||
"shim",
|
||||
"<jit>",
|
||||
);
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut invoke: Option<
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
> = None;
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
} else if method_id as u32 == 1 {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(ib) = arr
|
||||
.length()
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::IntegerBox>()
|
||||
{
|
||||
return ib.value;
|
||||
}
|
||||
}
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
return sb.value.len() as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut native_array_len: Option<i64> = None;
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
{
|
||||
if a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = a0 as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
} else if let Some(arr) =
|
||||
b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
if method_id as u32 == 1 {
|
||||
if let Some(ib) = arr
|
||||
.length()
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::IntegerBox>()
|
||||
{
|
||||
native_array_len = Some(ib.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if invoke.is_none() {
|
||||
if let Some(v) = native_array_len {
|
||||
if trace {
|
||||
eprintln!("[JIT-SHIM i64] native_fallback return {}", v);
|
||||
}
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({ "id": "shim.native.i64", "type_id": type_id, "method_id": method_id, "argc": argc, "ret": v }),
|
||||
"shim",
|
||||
"<jit>",
|
||||
);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
if invoke.is_none() {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
for v in args.iter() {
|
||||
if let crate::backend::vm::VMValue::BoxRef(b) = v {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() {
|
||||
return 0;
|
||||
}
|
||||
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(
|
||||
(argc.saturating_sub(1).max(0) as u16),
|
||||
);
|
||||
let mut add_i64 = |v: i64| {
|
||||
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v);
|
||||
};
|
||||
if argc >= 2 {
|
||||
add_i64(a1);
|
||||
}
|
||||
if argc >= 3 {
|
||||
add_i64(a2);
|
||||
}
|
||||
let mut out = vec![0xCDu8; 4096 + 32];
|
||||
let canary_val = 0xABu8;
|
||||
let canary_len = 16usize;
|
||||
for i in 0..canary_len {
|
||||
out[i] = canary_val;
|
||||
}
|
||||
for i in 0..canary_len {
|
||||
out[4096 + canary_len + i] = canary_val;
|
||||
}
|
||||
let mut out_len: usize = 0;
|
||||
let ok = unsafe {
|
||||
(invoke.unwrap())(
|
||||
instance_id,
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
buf.as_ptr(),
|
||||
buf.len(),
|
||||
out.as_mut_ptr().add(canary_len),
|
||||
&mut out_len as *mut usize,
|
||||
)
|
||||
};
|
||||
if ok != 0 {
|
||||
let out_slice = &out[canary_len..(canary_len + out_len.min(4096))];
|
||||
if let Some((tag, _sz, payload)) =
|
||||
crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice)
|
||||
{
|
||||
match tag {
|
||||
3 => {
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
}
|
||||
8 => {
|
||||
if _sz == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
let meta_opt =
|
||||
crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type);
|
||||
let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt {
|
||||
(meta.box_type.clone(), meta.invoke_fn)
|
||||
} else {
|
||||
("PluginBox".to_string(), invoke.unwrap())
|
||||
};
|
||||
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
box_type_name,
|
||||
r_type,
|
||||
r_inst,
|
||||
invoke_ptr,
|
||||
);
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
return if crate::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if _sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub(crate) extern "C" fn nyash_plugin_invoke3_f64(
|
||||
_type_id: i64,
|
||||
_method_id: i64,
|
||||
_argc: i64,
|
||||
_a0: i64,
|
||||
_a1: i64,
|
||||
_a2: i64,
|
||||
) -> f64 {
|
||||
0.0
|
||||
}
|
||||
|
||||
// === By-name plugin shims (i64) ===
|
||||
pub(crate) extern "C" fn nyash_plugin_invoke_name_getattr_i64(
|
||||
argc: i64,
|
||||
a0: i64,
|
||||
a1: i64,
|
||||
a2: i64,
|
||||
) -> i64 {
|
||||
nyash_plugin_invoke_name_common_i64("getattr", argc, a0, a1, a2)
|
||||
}
|
||||
pub(crate) extern "C" fn nyash_plugin_invoke_name_call_i64(
|
||||
argc: i64,
|
||||
a0: i64,
|
||||
a1: i64,
|
||||
a2: i64,
|
||||
) -> i64 {
|
||||
nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2)
|
||||
}
|
||||
|
||||
fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 {
|
||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut type_id: u32 = 0;
|
||||
let mut box_type: Option<String> = None;
|
||||
let mut invoke: Option<
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
|
||||
> = None;
|
||||
if a0 > 0 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(a0 as u64) {
|
||||
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
type_id = p.inner.type_id;
|
||||
box_type = Some(p.box_type.clone());
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1")
|
||||
{
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = a0.max(0) as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
type_id = p.inner.type_id;
|
||||
box_type = Some(p.box_type.clone());
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
for v in args.iter() {
|
||||
if let crate::backend::vm::VMValue::BoxRef(b) = v {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
type_id = p.inner.type_id;
|
||||
box_type = Some(p.box_type.clone());
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() {
|
||||
return 0;
|
||||
}
|
||||
let box_type = box_type.unwrap_or_default();
|
||||
let mh =
|
||||
if let Ok(host) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
host.resolve_method(&box_type, method)
|
||||
} else {
|
||||
return 0;
|
||||
};
|
||||
let method_id = match mh {
|
||||
Ok(h) => h.method_id,
|
||||
Err(_) => return 0,
|
||||
} as u32;
|
||||
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(
|
||||
(argc.saturating_sub(1).max(0) as u16),
|
||||
);
|
||||
let mut add_from_legacy = |pos: usize| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
if let Some(v) = args.get(pos) {
|
||||
match v {
|
||||
crate::backend::vm::VMValue::Integer(i) => {
|
||||
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, *i)
|
||||
}
|
||||
crate::backend::vm::VMValue::Float(f) => {
|
||||
crate::runtime::plugin_ffi_common::encode::f64(&mut buf, *f)
|
||||
}
|
||||
crate::backend::vm::VMValue::Bool(b) => {
|
||||
crate::runtime::plugin_ffi_common::encode::bool(&mut buf, *b)
|
||||
}
|
||||
crate::backend::vm::VMValue::BoxRef(_) => {
|
||||
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, 0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if argc >= 2 {
|
||||
add_from_legacy(1);
|
||||
}
|
||||
if argc >= 3 {
|
||||
add_from_legacy(2);
|
||||
}
|
||||
let mut out = vec![0u8; 4096];
|
||||
let mut out_len: usize = 0;
|
||||
let ok = unsafe {
|
||||
(invoke.unwrap())(
|
||||
instance_id,
|
||||
type_id as u32,
|
||||
method_id as u32,
|
||||
buf.as_ptr(),
|
||||
buf.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len as *mut usize,
|
||||
)
|
||||
};
|
||||
if ok != 0 {
|
||||
let out_slice = &out[0..out_len.min(4096)];
|
||||
if let Some((tag, _sz, payload)) =
|
||||
crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice)
|
||||
{
|
||||
match tag {
|
||||
3 => {
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
return i64::from_le_bytes(b);
|
||||
}
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) {
|
||||
return v as i64;
|
||||
}
|
||||
}
|
||||
8 => {
|
||||
if _sz == 8 {
|
||||
let mut t = [0u8; 4];
|
||||
t.copy_from_slice(&payload[0..4]);
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let r_type = u32::from_le_bytes(t);
|
||||
let r_inst = u32::from_le_bytes(i);
|
||||
let meta_opt =
|
||||
crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type);
|
||||
let (meta_box, invoke_ptr) = if let Some(meta) = meta_opt {
|
||||
(meta.box_type, meta.invoke_fn)
|
||||
} else {
|
||||
(box_type.clone(), invoke.unwrap())
|
||||
};
|
||||
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(
|
||||
meta_box, r_type, r_inst, invoke_ptr,
|
||||
);
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> =
|
||||
std::sync::Arc::new(pb);
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
return if crate::runtime::plugin_ffi_common::decode::bool(payload)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if _sz == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
119
archive/jit-cranelift/src/jit/lower/builder/tls.rs
Normal file
119
archive/jit-cranelift/src/jit/lower/builder/tls.rs
Normal file
@ -0,0 +1,119 @@
|
||||
#![cfg(feature = "cranelift-jit")]
|
||||
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
use cranelift_module::Module;
|
||||
|
||||
// TLS: 単一関数あたり1つの FunctionBuilder を保持(jit-direct 専用)
|
||||
pub(crate) mod clif_tls {
|
||||
use super::*;
|
||||
thread_local! {
|
||||
pub static FB: std::cell::RefCell<Option<TlsCtx>> = std::cell::RefCell::new(None);
|
||||
}
|
||||
pub struct TlsCtx {
|
||||
pub ctx: Box<cranelift_codegen::Context>,
|
||||
pub fbc: Box<cranelift_frontend::FunctionBuilderContext>,
|
||||
pub(crate) fb: *mut cranelift_frontend::FunctionBuilder<'static>,
|
||||
}
|
||||
impl TlsCtx {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ctx: Box::new(cranelift_codegen::Context::new()),
|
||||
fbc: Box::new(cranelift_frontend::FunctionBuilderContext::new()),
|
||||
fb: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
pub unsafe fn create(&mut self) {
|
||||
let func_ptr: *mut cranelift_codegen::ir::Function = &mut self.ctx.func;
|
||||
let fbc_ptr: *mut cranelift_frontend::FunctionBuilderContext = &mut *self.fbc;
|
||||
let fb = Box::new(cranelift_frontend::FunctionBuilder::new(
|
||||
&mut *func_ptr,
|
||||
&mut *fbc_ptr,
|
||||
));
|
||||
self.fb = Box::into_raw(fb);
|
||||
}
|
||||
pub fn with<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut cranelift_frontend::FunctionBuilder<'static>) -> R,
|
||||
) -> R {
|
||||
unsafe { f(&mut *self.fb) }
|
||||
}
|
||||
pub unsafe fn finalize_drop(&mut self) {
|
||||
if !self.fb.is_null() {
|
||||
let fb = Box::from_raw(self.fb);
|
||||
fb.finalize();
|
||||
self.fb = core::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
/// Finalize the current FunctionBuilder and take ownership of the underlying Context.
|
||||
pub fn take_context(&mut self) -> cranelift_codegen::Context {
|
||||
unsafe {
|
||||
self.finalize_drop();
|
||||
}
|
||||
// Move the current context out and replace with a fresh one
|
||||
let old = std::mem::replace(&mut self.ctx, Box::new(cranelift_codegen::Context::new()));
|
||||
*old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Small TLS helpers to call imported functions via the single FunctionBuilder
|
||||
pub(crate) fn tls_call_import_ret(
|
||||
module: &mut cranelift_jit::JITModule,
|
||||
func_id: cranelift_module::FuncId,
|
||||
args: &[cranelift_codegen::ir::Value],
|
||||
has_ret: bool,
|
||||
) -> Option<cranelift_codegen::ir::Value> {
|
||||
clif_tls::FB.with(|cell| {
|
||||
let mut opt = cell.borrow_mut();
|
||||
let tls = opt.as_mut().expect("FunctionBuilder TLS not initialized");
|
||||
tls.with(|fb| {
|
||||
// Guard: avoid emitting a verifier-invalid call when args are unexpectedly empty.
|
||||
// Some early shims (e.g., instrumentation) may have declared a 1-arity import;
|
||||
// if lowering produced no arguments, synthesize a zero literal when a return is expected,
|
||||
// and skip the call entirely to keep the IR valid.
|
||||
if args.is_empty() {
|
||||
if has_ret {
|
||||
use cranelift_codegen::ir::types;
|
||||
return Some(fb.ins().iconst(types::I64, 0));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let fref = module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, args);
|
||||
if has_ret {
|
||||
fb.inst_results(call_inst).get(0).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn tls_call_import_with_iconsts(
|
||||
module: &mut cranelift_jit::JITModule,
|
||||
func_id: cranelift_module::FuncId,
|
||||
iconsts: &[i64],
|
||||
tail_args: &[cranelift_codegen::ir::Value],
|
||||
has_ret: bool,
|
||||
) -> Option<cranelift_codegen::ir::Value> {
|
||||
use cranelift_codegen::ir::types;
|
||||
clif_tls::FB.with(|cell| {
|
||||
let mut opt = cell.borrow_mut();
|
||||
let tls = opt.as_mut().expect("FunctionBuilder TLS not initialized");
|
||||
tls.with(|fb| {
|
||||
let mut all_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for &c in iconsts {
|
||||
all_args.push(fb.ins().iconst(types::I64, c));
|
||||
}
|
||||
all_args.extend_from_slice(tail_args);
|
||||
let fref = module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &all_args);
|
||||
if has_ret {
|
||||
fb.inst_results(call_inst).get(0).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
91
archive/jit-cranelift/src/jit/lower/cfg_dot.rs
Normal file
91
archive/jit-cranelift/src/jit/lower/cfg_dot.rs
Normal file
@ -0,0 +1,91 @@
|
||||
pub fn dump_cfg_dot(
|
||||
func: &crate::mir::MirFunction,
|
||||
path: &str,
|
||||
phi_min: bool,
|
||||
) -> std::io::Result<()> {
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!("digraph \"{}\" {{\n", func.signature.name));
|
||||
out.push_str(" node [shape=box, fontsize=10];\n");
|
||||
// Derive simple bool sets: compare dsts are bool; phi of all-bool inputs are bool
|
||||
let mut bool_values: std::collections::HashSet<crate::mir::ValueId> =
|
||||
std::collections::HashSet::new();
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Compare { dst, .. } = ins {
|
||||
bool_values.insert(*dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut bool_phi: std::collections::HashSet<crate::mir::ValueId> =
|
||||
std::collections::HashSet::new();
|
||||
if phi_min {
|
||||
for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
||||
if !inputs.is_empty() && inputs.iter().all(|(_, v)| bool_values.contains(v)) {
|
||||
bool_phi.insert(*dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort blocks for deterministic output
|
||||
let mut bb_ids: Vec<_> = func.blocks.keys().copied().collect();
|
||||
bb_ids.sort_by_key(|b| b.0);
|
||||
// Emit nodes with labels
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
let phi_count = bb
|
||||
.instructions
|
||||
.iter()
|
||||
.filter(|ins| matches!(ins, crate::mir::MirInstruction::Phi { .. }))
|
||||
.count();
|
||||
let phi_b1_count = bb
|
||||
.instructions
|
||||
.iter()
|
||||
.filter(|ins| match ins {
|
||||
crate::mir::MirInstruction::Phi { dst, .. } => bool_phi.contains(dst),
|
||||
_ => false,
|
||||
})
|
||||
.count();
|
||||
let mut label = format!("bb{}", bb_id.0);
|
||||
if phi_min && phi_count > 0 {
|
||||
if phi_b1_count > 0 {
|
||||
label = format!("{}\\nphi:{} (b1:{})", label, phi_count, phi_b1_count);
|
||||
} else {
|
||||
label = format!("{}\\nphi:{}", label, phi_count);
|
||||
}
|
||||
}
|
||||
if *bb_id == func.entry_block {
|
||||
label = format!("{}\\nENTRY", label);
|
||||
}
|
||||
out.push_str(&format!(" n{} [label=\"{}\"];\n", bb_id.0, label));
|
||||
}
|
||||
// Emit edges based on terminators
|
||||
for bb_id in bb_ids.iter() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
crate::mir::MirInstruction::Jump { target } => {
|
||||
out.push_str(&format!(" n{} -> n{};\n", bb_id.0, target.0));
|
||||
}
|
||||
crate::mir::MirInstruction::Branch {
|
||||
then_bb, else_bb, ..
|
||||
} => {
|
||||
// Branch condition is boolean (b1)
|
||||
out.push_str(&format!(
|
||||
" n{} -> n{} [label=\"then cond:b1\"];\n",
|
||||
bb_id.0, then_bb.0
|
||||
));
|
||||
out.push_str(&format!(
|
||||
" n{} -> n{} [label=\"else cond:b1\"];\n",
|
||||
bb_id.0, else_bb.0
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.push_str("}\n");
|
||||
std::fs::write(path, out)
|
||||
}
|
||||
1799
archive/jit-cranelift/src/jit/lower/core.rs
Normal file
1799
archive/jit-cranelift/src/jit/lower/core.rs
Normal file
File diff suppressed because it is too large
Load Diff
140
archive/jit-cranelift/src/jit/lower/core/analysis.rs
Normal file
140
archive/jit-cranelift/src/jit/lower/core/analysis.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
|
||||
// removed unused imports
|
||||
use super::LowerCore;
|
||||
|
||||
impl LowerCore {
|
||||
pub(crate) fn analyze(&mut self, func: &MirFunction, bb_ids: &Vec<BasicBlockId>) {
|
||||
// Seed boolean lattice with boolean parameters from MIR signature
|
||||
if !func.signature.params.is_empty() {
|
||||
for (idx, vid) in func.params.iter().copied().enumerate() {
|
||||
if let Some(mt) = func.signature.params.get(idx) {
|
||||
if matches!(mt, crate::mir::MirType::Bool) {
|
||||
self.bool_values.insert(vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pre-scan to classify boolean-producing values and propagate via Copy/Phi/Load-Store heuristics.
|
||||
self.bool_values.clear();
|
||||
let mut copy_edges: Vec<(ValueId, ValueId)> = Vec::new();
|
||||
let mut phi_defs: Vec<(ValueId, Vec<ValueId>)> = Vec::new();
|
||||
let mut stores: Vec<(ValueId, ValueId)> = Vec::new(); // (ptr, value)
|
||||
let mut loads: Vec<(ValueId, ValueId)> = Vec::new(); // (dst, ptr)
|
||||
for bb in bb_ids.iter() {
|
||||
if let Some(block) = func.blocks.get(bb) {
|
||||
for ins in block.instructions.iter() {
|
||||
match ins {
|
||||
MirInstruction::Compare { dst, .. } => {
|
||||
self.bool_values.insert(*dst);
|
||||
}
|
||||
MirInstruction::Const { dst, value } => {
|
||||
if let crate::mir::ConstValue::Bool(_) = value {
|
||||
self.bool_values.insert(*dst);
|
||||
}
|
||||
}
|
||||
MirInstruction::Copy { dst, src } => {
|
||||
copy_edges.push((*dst, *src));
|
||||
}
|
||||
MirInstruction::Phi { dst, inputs } => {
|
||||
self.phi_values.insert(*dst);
|
||||
let ins: Vec<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
||||
phi_defs.push((*dst, ins));
|
||||
}
|
||||
MirInstruction::Store { ptr, value } => {
|
||||
stores.push((*ptr, *value));
|
||||
}
|
||||
MirInstruction::Load { dst, ptr } => {
|
||||
loads.push((*dst, *ptr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fixed-point propagation
|
||||
let mut store_bool_ptrs: HashSet<ValueId> = HashSet::new();
|
||||
let mut changed = true;
|
||||
while changed {
|
||||
changed = false;
|
||||
// Copy propagation
|
||||
for (dst, src) in copy_edges.iter().copied() {
|
||||
if self.bool_values.contains(&src) && !self.bool_values.contains(&dst) {
|
||||
self.bool_values.insert(dst);
|
||||
changed = true;
|
||||
}
|
||||
if store_bool_ptrs.contains(&src) && !store_bool_ptrs.contains(&dst) {
|
||||
store_bool_ptrs.insert(dst);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Store marking
|
||||
for (ptr, val) in stores.iter().copied() {
|
||||
if self.bool_values.contains(&val) && !store_bool_ptrs.contains(&ptr) {
|
||||
store_bool_ptrs.insert(ptr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Load propagation
|
||||
for (dst, ptr) in loads.iter().copied() {
|
||||
if store_bool_ptrs.contains(&ptr) && !self.bool_values.contains(&dst) {
|
||||
self.bool_values.insert(dst);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// PHI closure for value booleans
|
||||
for (dst, inputs) in phi_defs.iter() {
|
||||
if inputs.iter().all(|v| self.bool_values.contains(v))
|
||||
&& !self.bool_values.contains(dst)
|
||||
{
|
||||
self.bool_values.insert(*dst);
|
||||
self.bool_phi_values.insert(*dst);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// PHI closure for pointer aliases
|
||||
for (dst, inputs) in phi_defs.iter() {
|
||||
if inputs.iter().all(|v| store_bool_ptrs.contains(v))
|
||||
&& !store_bool_ptrs.contains(dst)
|
||||
{
|
||||
store_bool_ptrs.insert(*dst);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// PHI statistics
|
||||
let mut total_phi_slots: usize = 0;
|
||||
let mut total_phi_b1_slots: usize = 0;
|
||||
for (dst, inputs) in phi_defs.iter() {
|
||||
total_phi_slots += 1;
|
||||
let used_as_branch = func.blocks.values().any(|bbx| {
|
||||
if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator {
|
||||
condition == dst
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
let is_b1 = self.bool_phi_values.contains(dst)
|
||||
|| inputs.iter().all(|v| {
|
||||
self.bool_values.contains(v)
|
||||
|| self
|
||||
.known_i64
|
||||
.get(v)
|
||||
.map(|&iv| iv == 0 || iv == 1)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
|| used_as_branch;
|
||||
if is_b1 {
|
||||
total_phi_b1_slots += 1;
|
||||
}
|
||||
}
|
||||
if total_phi_slots > 0 {
|
||||
crate::jit::rt::phi_total_inc(total_phi_slots as u64);
|
||||
crate::jit::rt::phi_b1_inc(total_phi_b1_slots as u64);
|
||||
self.last_phi_total = total_phi_slots as u64;
|
||||
self.last_phi_b1 = total_phi_b1_slots as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
284
archive/jit-cranelift/src/jit/lower/core/cfg.rs
Normal file
284
archive/jit-cranelift/src/jit/lower/core/cfg.rs
Normal file
@ -0,0 +1,284 @@
|
||||
use super::super::builder::IRBuilder;
|
||||
use super::LowerCore;
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction};
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl LowerCore {
|
||||
pub(crate) fn build_phi_succords(
|
||||
&mut self,
|
||||
func: &MirFunction,
|
||||
bb_ids: &Vec<BasicBlockId>,
|
||||
builder: &mut dyn IRBuilder,
|
||||
enable_phi_min: bool,
|
||||
) -> HashMap<BasicBlockId, Vec<crate::mir::ValueId>> {
|
||||
let mut succ_phi_order: HashMap<BasicBlockId, Vec<crate::mir::ValueId>> = HashMap::new();
|
||||
if !enable_phi_min {
|
||||
return succ_phi_order;
|
||||
}
|
||||
for (bb_id, bb) in func.blocks.iter() {
|
||||
let mut order: Vec<crate::mir::ValueId> = Vec::new();
|
||||
for ins in bb.instructions.iter() {
|
||||
if let MirInstruction::Phi { dst, .. } = ins {
|
||||
order.push(*dst);
|
||||
}
|
||||
}
|
||||
if !order.is_empty() {
|
||||
succ_phi_order.insert(*bb_id, order);
|
||||
}
|
||||
}
|
||||
// Pre-declare block parameter counts per successor to avoid late appends
|
||||
for (succ, order) in succ_phi_order.iter() {
|
||||
if let Some(idx) = bb_ids.iter().position(|x| x == succ) {
|
||||
builder.ensure_block_params_i64(idx, order.len());
|
||||
}
|
||||
}
|
||||
succ_phi_order
|
||||
}
|
||||
|
||||
pub(crate) fn dump_phi_cfg(
|
||||
&self,
|
||||
succ_phi_order: &HashMap<BasicBlockId, Vec<crate::mir::ValueId>>,
|
||||
func: &MirFunction,
|
||||
blocks_len: usize,
|
||||
enable_phi_min: bool,
|
||||
) {
|
||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() != Some("1") {
|
||||
return;
|
||||
}
|
||||
let succs = succ_phi_order.len();
|
||||
eprintln!(
|
||||
"[JIT] cfg: blocks={} phi_succ={} (phi_min={})",
|
||||
blocks_len, succs, enable_phi_min
|
||||
);
|
||||
if enable_phi_min {
|
||||
let mut total_phi_slots: usize = 0;
|
||||
let mut total_phi_b1_slots: usize = 0;
|
||||
for (succ, order) in succ_phi_order.iter() {
|
||||
let mut preds_set: std::collections::BTreeSet<i64> =
|
||||
std::collections::BTreeSet::new();
|
||||
let mut phi_lines: Vec<String> = Vec::new();
|
||||
if let Some(bb_succ) = func.blocks.get(succ) {
|
||||
for ins in bb_succ.instructions.iter() {
|
||||
if let MirInstruction::Phi { dst, inputs } = ins {
|
||||
for (pred, _) in inputs.iter() {
|
||||
preds_set.insert(pred.0 as i64);
|
||||
}
|
||||
let mut pairs: Vec<String> = Vec::new();
|
||||
for (pred, val) in inputs.iter() {
|
||||
pairs.push(format!("{}:{}", pred.0, val.0));
|
||||
}
|
||||
let used_as_branch = func.blocks.values().any(|bbx| {
|
||||
if let Some(MirInstruction::Branch { condition, .. }) =
|
||||
&bbx.terminator
|
||||
{
|
||||
condition == dst
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
let is_b1 = self.bool_phi_values.contains(dst)
|
||||
|| inputs.iter().all(|(_, v)| {
|
||||
self.bool_values.contains(v)
|
||||
|| self
|
||||
.known_i64
|
||||
.get(v)
|
||||
.map(|&iv| iv == 0 || iv == 1)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
|| used_as_branch;
|
||||
if is_b1 {
|
||||
total_phi_b1_slots += 1;
|
||||
}
|
||||
total_phi_slots += 1;
|
||||
phi_lines.push(format!(
|
||||
" phi: bb={} dst={} inputs=[{}] (b1={})",
|
||||
succ.0,
|
||||
dst.0,
|
||||
pairs.join(","),
|
||||
is_b1
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let preds_list: Vec<String> =
|
||||
preds_set.into_iter().map(|p| p.to_string()).collect();
|
||||
eprintln!(
|
||||
"[JIT] phi: bb={} slots={} preds={}",
|
||||
succ.0,
|
||||
order.len(),
|
||||
preds_list.join("|")
|
||||
);
|
||||
for ln in phi_lines {
|
||||
eprintln!("[JIT]{}", ln);
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
"[JIT] phi_summary: total_slots={} b1_slots={}",
|
||||
total_phi_slots, total_phi_b1_slots
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerCore {
|
||||
/// Lower a Branch terminator, including fast-path select+return and PHI(min) argument wiring.
|
||||
pub(crate) fn lower_branch_terminator(
|
||||
&mut self,
|
||||
builder: &mut dyn IRBuilder,
|
||||
func: &MirFunction,
|
||||
bb_ids: &Vec<BasicBlockId>,
|
||||
bb_id: BasicBlockId,
|
||||
condition: &crate::mir::ValueId,
|
||||
then_bb: &BasicBlockId,
|
||||
else_bb: &BasicBlockId,
|
||||
succ_phi_order: &HashMap<BasicBlockId, Vec<crate::mir::ValueId>>,
|
||||
enable_phi_min: bool,
|
||||
) {
|
||||
// Fast-path: if both successors immediately return known i64 constants, lower as select+return
|
||||
let mut fastpath_done = false;
|
||||
let succ_returns_const = |succ: &crate::mir::BasicBlock| -> Option<i64> {
|
||||
use crate::mir::MirInstruction as I;
|
||||
if let Some(I::Return { value: Some(v) }) = &succ.terminator {
|
||||
for ins in succ.instructions.iter() {
|
||||
if let I::Const { dst, value } = ins {
|
||||
if dst == v {
|
||||
if let crate::mir::ConstValue::Integer(k) = value {
|
||||
return Some(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
if let (Some(bb_then), Some(bb_else)) = (func.blocks.get(then_bb), func.blocks.get(else_bb))
|
||||
{
|
||||
if let (Some(k_then), Some(k_else)) =
|
||||
(succ_returns_const(bb_then), succ_returns_const(bb_else))
|
||||
{
|
||||
self.push_value_if_known_or_param(builder, condition);
|
||||
builder.emit_const_i64(k_then);
|
||||
builder.emit_const_i64(k_else);
|
||||
builder.emit_select_i64();
|
||||
builder.emit_return();
|
||||
fastpath_done = true;
|
||||
}
|
||||
}
|
||||
if fastpath_done {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, emit CFG branch with optional PHI(min) argument wiring
|
||||
self.push_value_if_known_or_param(builder, condition);
|
||||
let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0);
|
||||
let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0);
|
||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[LowerCore] br_if: cur_bb={} then_idx={} else_idx={}",
|
||||
bb_id.0, then_index, else_index
|
||||
);
|
||||
}
|
||||
if enable_phi_min {
|
||||
let mut then_n = 0usize;
|
||||
let mut else_n = 0usize;
|
||||
if let Some(order) = succ_phi_order.get(then_bb) {
|
||||
let mut cnt = 0usize;
|
||||
if let Some(bb_succ) = func.blocks.get(then_bb) {
|
||||
for dst in order.iter() {
|
||||
for ins in bb_succ.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||
if d2 == dst {
|
||||
if let Some((_, val)) =
|
||||
inputs.iter().find(|(pred, _)| pred == &bb_id)
|
||||
{
|
||||
self.push_value_if_known_or_param(builder, val);
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cnt > 0 {
|
||||
builder.ensure_block_params_i64(then_index, cnt);
|
||||
}
|
||||
then_n = cnt;
|
||||
}
|
||||
if let Some(order) = succ_phi_order.get(else_bb) {
|
||||
let mut cnt = 0usize;
|
||||
if let Some(bb_succ) = func.blocks.get(else_bb) {
|
||||
for dst in order.iter() {
|
||||
for ins in bb_succ.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||
if d2 == dst {
|
||||
if let Some((_, val)) =
|
||||
inputs.iter().find(|(pred, _)| pred == &bb_id)
|
||||
{
|
||||
self.push_value_if_known_or_param(builder, val);
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cnt > 0 {
|
||||
builder.ensure_block_params_i64(else_index, cnt);
|
||||
}
|
||||
else_n = cnt;
|
||||
}
|
||||
builder.br_if_with_args(then_index, else_index, then_n, else_n);
|
||||
} else {
|
||||
builder.br_if_top_is_true(then_index, else_index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower a Jump terminator with optional PHI(min) argument wiring.
|
||||
pub(crate) fn lower_jump_terminator(
|
||||
&mut self,
|
||||
builder: &mut dyn IRBuilder,
|
||||
func: &MirFunction,
|
||||
bb_ids: &Vec<BasicBlockId>,
|
||||
bb_id: BasicBlockId,
|
||||
target: &BasicBlockId,
|
||||
succ_phi_order: &HashMap<BasicBlockId, Vec<crate::mir::ValueId>>,
|
||||
enable_phi_min: bool,
|
||||
) {
|
||||
let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0);
|
||||
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[LowerCore] jump: cur_bb={} target_idx={}",
|
||||
bb_id.0, target_index
|
||||
);
|
||||
}
|
||||
if enable_phi_min {
|
||||
let mut n = 0usize;
|
||||
if let Some(order) = succ_phi_order.get(target) {
|
||||
let mut cnt = 0usize;
|
||||
if let Some(bb_succ) = func.blocks.get(target) {
|
||||
for dst in order.iter() {
|
||||
for ins in bb_succ.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
||||
if d2 == dst {
|
||||
if let Some((_, val)) =
|
||||
inputs.iter().find(|(pred, _)| pred == &bb_id)
|
||||
{
|
||||
self.push_value_if_known_or_param(builder, val);
|
||||
cnt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if cnt > 0 {
|
||||
builder.ensure_block_params_i64(target_index, cnt);
|
||||
}
|
||||
n = cnt;
|
||||
}
|
||||
builder.jump_with_args(target_index, n);
|
||||
} else {
|
||||
builder.jump_to(target_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
1386
archive/jit-cranelift/src/jit/lower/core/ops_ext.rs
Normal file
1386
archive/jit-cranelift/src/jit/lower/core/ops_ext.rs
Normal file
File diff suppressed because it is too large
Load Diff
157
archive/jit-cranelift/src/jit/lower/core/string_len.rs
Normal file
157
archive/jit-cranelift/src/jit/lower/core/string_len.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use super::super::builder::IRBuilder;
|
||||
use super::LowerCore;
|
||||
|
||||
impl LowerCore {
|
||||
/// Emit robust length retrieval with fallback for String/Any:
|
||||
/// 1) Prefer `nyash.string.len_h(recv)`
|
||||
/// 2) If that yields 0 at runtime, select `nyash.any.length_h(recv)`
|
||||
/// Returns: pushes selected length (i64) onto builder stack.
|
||||
pub(super) fn emit_len_with_fallback_param(&mut self, b: &mut dyn IRBuilder, pidx: usize) {
|
||||
use super::super::builder::CmpKind;
|
||||
// Temp locals
|
||||
let hslot = self.next_local;
|
||||
self.next_local += 1; // receiver handle slot
|
||||
let t_string = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_any = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_cond = self.next_local;
|
||||
self.next_local += 1;
|
||||
// Materialize receiver handle from param index
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true);
|
||||
b.store_local_i64(hslot);
|
||||
// String.len_h
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_STRING_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_param",
|
||||
);
|
||||
b.load_local_i64(hslot);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
// debug: observe string len
|
||||
b.emit_debug_i64_local(1100, t_string);
|
||||
// Any.length_h
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_param",
|
||||
);
|
||||
b.load_local_i64(hslot);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
// debug: observe any len
|
||||
b.emit_debug_i64_local(1101, t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
// debug: observe condition
|
||||
b.emit_debug_i64_local(1102, t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond); // cond (bottom)
|
||||
b.load_local_i64(t_any); // then
|
||||
b.load_local_i64(t_string); // else
|
||||
b.emit_select_i64();
|
||||
}
|
||||
|
||||
pub(super) fn emit_len_with_fallback_local_handle(
|
||||
&mut self,
|
||||
b: &mut dyn IRBuilder,
|
||||
slot: usize,
|
||||
) {
|
||||
use super::super::builder::CmpKind;
|
||||
let t_string = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_any = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_cond = self.next_local;
|
||||
self.next_local += 1;
|
||||
// String.len_h
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_STRING_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_local",
|
||||
);
|
||||
b.load_local_i64(slot);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
b.emit_debug_i64_local(1200, t_string);
|
||||
// Any.length_h
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_local",
|
||||
);
|
||||
b.load_local_i64(slot);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
b.emit_debug_i64_local(1201, t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
b.emit_debug_i64_local(1202, t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond);
|
||||
b.load_local_i64(t_any);
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_select_i64();
|
||||
}
|
||||
|
||||
pub(super) fn emit_len_with_fallback_literal(&mut self, b: &mut dyn IRBuilder, s: &str) {
|
||||
use super::super::builder::CmpKind;
|
||||
let t_string = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_any = self.next_local;
|
||||
self.next_local += 1;
|
||||
let t_cond = self.next_local;
|
||||
self.next_local += 1;
|
||||
// String.len_h on literal handle
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_STRING_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_lit",
|
||||
);
|
||||
b.emit_string_handle_from_literal(s);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_LEN_H, 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
b.emit_debug_i64_local(1300, t_string);
|
||||
// Any.length_h on literal handle (recreate handle; safe in v0)
|
||||
crate::jit::observe::lower_hostcall(
|
||||
crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
1,
|
||||
&["Handle"],
|
||||
"allow",
|
||||
"core_len_lit",
|
||||
);
|
||||
b.emit_string_handle_from_literal(s);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
b.emit_debug_i64_local(1301, t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
b.emit_debug_i64_local(1302, t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond);
|
||||
b.load_local_i64(t_any);
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_select_i64();
|
||||
}
|
||||
}
|
||||
803
archive/jit-cranelift/src/jit/lower/core_hostcall.rs
Normal file
803
archive/jit-cranelift/src/jit/lower/core_hostcall.rs
Normal file
@ -0,0 +1,803 @@
|
||||
#![allow(unreachable_patterns, unused_variables)]
|
||||
//! HostCall-related lowering helpers split from core.rs (no behavior change)
|
||||
use super::builder::IRBuilder;
|
||||
use crate::mir::{MirFunction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn lower_array_get(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
array: &ValueId,
|
||||
index: &ValueId,
|
||||
) {
|
||||
if crate::jit::config::current().hostcall {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
let idx = known_i64.get(index).copied().unwrap_or(0);
|
||||
if let Some(pidx) = param_index.get(array).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_const_i64(idx);
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_GET_H
|
||||
};
|
||||
b.emit_host_call(sym, 2, true);
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_const_i64(idx);
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_GET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_GET
|
||||
};
|
||||
b.emit_host_call(sym, 2, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_size_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
recv: &ValueId,
|
||||
dst_is_some: bool,
|
||||
) {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SIZE
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_MAP_SIZE_H
|
||||
};
|
||||
b.emit_host_call(sym, 1, dst_is_some);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_get_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
key: &ValueId,
|
||||
dst_is_some: bool,
|
||||
) {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
if let Some(i) = known_i64.get(key).copied() {
|
||||
b.emit_const_i64(i);
|
||||
} else if let Some(kp) = param_index.get(key).copied() {
|
||||
b.emit_param_i64(kp);
|
||||
} else {
|
||||
b.emit_const_i64(0);
|
||||
}
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_MAP_GET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_H
|
||||
};
|
||||
b.emit_host_call(sym, 2, dst_is_some);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_has_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
key: &ValueId,
|
||||
dst_is_some: bool,
|
||||
) {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
if let Some(i) = known_i64.get(key).copied() {
|
||||
b.emit_const_i64(i);
|
||||
} else if let Some(kp) = param_index.get(key).copied() {
|
||||
b.emit_param_i64(kp);
|
||||
} else {
|
||||
b.emit_const_i64(0);
|
||||
}
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_MAP_HAS
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_MAP_HAS_H
|
||||
};
|
||||
b.emit_host_call(sym, 2, dst_is_some);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_set_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
key: &ValueId,
|
||||
value: &ValueId,
|
||||
) {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
if let Some(i) = known_i64.get(key).copied() {
|
||||
b.emit_const_i64(i);
|
||||
} else if let Some(kp) = param_index.get(key).copied() {
|
||||
b.emit_param_i64(kp);
|
||||
} else {
|
||||
b.emit_const_i64(0);
|
||||
}
|
||||
if let Some(i) = known_i64.get(value).copied() {
|
||||
b.emit_const_i64(i);
|
||||
} else if let Some(vp) = param_index.get(value).copied() {
|
||||
b.emit_param_i64(vp);
|
||||
} else {
|
||||
b.emit_const_i64(0);
|
||||
}
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_MAP_SET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_MAP_SET_H
|
||||
};
|
||||
b.emit_host_call(sym, 3, false);
|
||||
}
|
||||
}
|
||||
pub fn lower_array_set(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
array: &ValueId,
|
||||
index: &ValueId,
|
||||
value: &ValueId,
|
||||
) {
|
||||
if crate::jit::config::current().hostcall {
|
||||
let use_bridge = std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1");
|
||||
let idx = known_i64.get(index).copied().unwrap_or(0);
|
||||
let val = known_i64.get(value).copied().unwrap_or(0);
|
||||
if let Some(pidx) = param_index.get(array).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_const_i64(idx);
|
||||
b.emit_const_i64(val);
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_SET_H
|
||||
};
|
||||
b.emit_host_call(sym, 3, false);
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_const_i64(idx);
|
||||
b.emit_const_i64(val);
|
||||
let sym = if use_bridge {
|
||||
crate::jit::r#extern::host_bridge::SYM_HOST_ARRAY_SET
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_SET
|
||||
};
|
||||
b.emit_host_call(sym, 3, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_box_call(
|
||||
func: &MirFunction,
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
known_f64: &HashMap<ValueId, f64>,
|
||||
float_box_values: &std::collections::HashSet<ValueId>,
|
||||
recv: &ValueId,
|
||||
method: &str,
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) {
|
||||
if !crate::jit::config::current().hostcall {
|
||||
return;
|
||||
}
|
||||
match method {
|
||||
"len" | "length" => {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["I64(index)"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
// Pass parameter index directly (JIT thunks read legacy VM args by index)
|
||||
b.emit_param_i64(pidx as i64 as usize);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_LEN,
|
||||
1,
|
||||
dst.is_some(),
|
||||
);
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN,
|
||||
"decision": "fallback", "reason": "receiver_not_param",
|
||||
"argc": 1, "arg_types": ["I64(index)"]
|
||||
}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
b.emit_const_i64(-1);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_LEN,
|
||||
1,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
"isEmpty" | "is_empty" => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H,
|
||||
1,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// math.* family (read-only)
|
||||
m if m.starts_with("math.") => {
|
||||
let sym = format!("nyash.{}", m);
|
||||
use crate::jit::hostcall_registry::{check_signature, ArgKind};
|
||||
let mut observed: Vec<ArgKind> = Vec::new();
|
||||
for (i, v) in args.iter().enumerate() {
|
||||
let kind = if let Some(mt) = func.signature.params.get(i) {
|
||||
match mt {
|
||||
crate::mir::MirType::Float => ArgKind::F64,
|
||||
crate::mir::MirType::Integer => ArgKind::I64,
|
||||
crate::mir::MirType::Bool => ArgKind::I64,
|
||||
crate::mir::MirType::String | crate::mir::MirType::Box(_) => {
|
||||
ArgKind::Handle
|
||||
}
|
||||
_ => {
|
||||
if known_f64.contains_key(v) || float_box_values.contains(v) {
|
||||
ArgKind::F64
|
||||
} else {
|
||||
ArgKind::I64
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if known_f64.contains_key(v) || float_box_values.contains(v) {
|
||||
ArgKind::F64
|
||||
} else {
|
||||
ArgKind::I64
|
||||
}
|
||||
};
|
||||
observed.push(kind);
|
||||
}
|
||||
let arg_types: Vec<&'static str> = observed
|
||||
.iter()
|
||||
.map(|k| match k {
|
||||
ArgKind::I64 => "I64",
|
||||
ArgKind::F64 => "F64",
|
||||
ArgKind::Handle => "Handle",
|
||||
})
|
||||
.collect();
|
||||
match check_signature(&sym, &observed) {
|
||||
Ok(()) => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"allow", "reason":"sig_ok", "argc": observed.len(), "arg_types": arg_types}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
if crate::jit::config::current().native_f64 {
|
||||
let (symbol, arity) = match method {
|
||||
"math.sin" => ("nyash.math.sin_f64", 1),
|
||||
"math.cos" => ("nyash.math.cos_f64", 1),
|
||||
"math.abs" => ("nyash.math.abs_f64", 1),
|
||||
"math.min" => ("nyash.math.min_f64", 2),
|
||||
"math.max" => ("nyash.math.max_f64", 2),
|
||||
_ => ("nyash.math.sin_f64", 1),
|
||||
};
|
||||
for i in 0..arity {
|
||||
if let Some(v) = args.get(i) {
|
||||
if let Some(fv) = known_f64.get(v).copied() {
|
||||
b.emit_const_f64(fv);
|
||||
continue;
|
||||
}
|
||||
if let Some(iv) = known_i64.get(v).copied() {
|
||||
b.emit_const_f64(iv as f64);
|
||||
continue;
|
||||
}
|
||||
let mut emitted = false;
|
||||
'scan: for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
args: nb_args,
|
||||
} = ins
|
||||
{
|
||||
if *dst == *v && box_type == "FloatBox" {
|
||||
if let Some(srcv) = nb_args.get(0) {
|
||||
if let Some(fv) = known_f64.get(srcv).copied() {
|
||||
b.emit_const_f64(fv);
|
||||
emitted = true;
|
||||
break 'scan;
|
||||
}
|
||||
if let Some(iv) = known_i64.get(srcv).copied() {
|
||||
b.emit_const_f64(iv as f64);
|
||||
emitted = true;
|
||||
break 'scan;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !emitted {
|
||||
b.emit_const_f64(0.0);
|
||||
}
|
||||
} else {
|
||||
b.emit_const_f64(0.0);
|
||||
}
|
||||
}
|
||||
let kinds: Vec<super::builder::ParamKind> =
|
||||
(0..arity).map(|_| super::builder::ParamKind::F64).collect();
|
||||
b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true);
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed.len(), "arg_types": arg_types}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map/String/Array read methods and limited mutating (whitelist)
|
||||
_ => {
|
||||
// Whitelist-driven
|
||||
let pol = crate::jit::policy::current();
|
||||
match method {
|
||||
// String
|
||||
"charCodeAt" => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
// recvはHandle (param) を期待。indexはknown_i64でcoerce
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
let idx = args
|
||||
.get(0)
|
||||
.and_then(|v| known_i64.get(v).copied())
|
||||
.unwrap_or(0);
|
||||
b.emit_const_i64(idx);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Map
|
||||
"size" => {
|
||||
lower_map_size_simple(b, param_index, recv, dst.is_some());
|
||||
}
|
||||
"get" => {
|
||||
if let Some(k) = args.get(0) {
|
||||
lower_map_get_simple(b, param_index, known_i64, recv, k, dst.is_some());
|
||||
}
|
||||
}
|
||||
"has" => {
|
||||
if let Some(k) = args.get(0) {
|
||||
lower_map_has_simple(b, param_index, known_i64, recv, k, dst.is_some());
|
||||
}
|
||||
}
|
||||
"set" => {
|
||||
if args.len() >= 2 {
|
||||
lower_map_set_simple(b, param_index, known_i64, recv, &args[0], &args[1]);
|
||||
}
|
||||
}
|
||||
"has" => {
|
||||
// Decide on key kind via registry and known values
|
||||
use crate::jit::hostcall_registry::{check_signature, ArgKind};
|
||||
let canonical = "nyash.map.has".to_string();
|
||||
let mut observed_kinds: Vec<ArgKind> = Vec::new();
|
||||
observed_kinds.push(ArgKind::Handle);
|
||||
let key_vid = args.get(0).copied();
|
||||
let key_kind = if let Some(kv) = key_vid {
|
||||
if let Some(mt) = func.signature.params.iter().find(|_| true) {
|
||||
// heuristic; signature may not align
|
||||
match mt {
|
||||
crate::mir::MirType::Float => ArgKind::I64,
|
||||
crate::mir::MirType::Integer => ArgKind::I64,
|
||||
crate::mir::MirType::Bool => ArgKind::I64,
|
||||
crate::mir::MirType::String | crate::mir::MirType::Box(_) => {
|
||||
ArgKind::Handle
|
||||
}
|
||||
_ => ArgKind::Handle,
|
||||
}
|
||||
} else if let Some(_) = known_i64.get(&kv) {
|
||||
ArgKind::I64
|
||||
} else {
|
||||
ArgKind::Handle
|
||||
}
|
||||
} else {
|
||||
ArgKind::Handle
|
||||
};
|
||||
observed_kinds.push(key_kind);
|
||||
let arg_types: Vec<&'static str> = observed_kinds
|
||||
.iter()
|
||||
.map(|k| match k {
|
||||
ArgKind::I64 => "I64",
|
||||
ArgKind::F64 => "F64",
|
||||
ArgKind::Handle => "Handle",
|
||||
})
|
||||
.collect();
|
||||
let _ = check_signature(&canonical, &observed_kinds);
|
||||
// HH fast-path if key is a Handle and also a param; otherwise H/I64
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
if let Some(kv) = key_vid {
|
||||
match key_kind {
|
||||
ArgKind::I64 => {
|
||||
let kval = known_i64.get(&kv).copied().unwrap_or(0);
|
||||
b.emit_const_i64(kval);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_H,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
ArgKind::Handle => {
|
||||
if let Some(kp) = param_index.get(&kv).copied() {
|
||||
b.emit_param_i64(kp);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_HH,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Array (mutating)
|
||||
"push" | "set" => {
|
||||
let wh = &pol.hostcall_whitelist;
|
||||
let sym = if method == "push" {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_PUSH
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_ARRAY_SET
|
||||
};
|
||||
if wh.iter().any(|s| s == sym) {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"allow", "reason":"whitelist", "argc": args.len()}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
b.emit_host_call(sym, 2, false);
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating", "argc": args.len()}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (was: lower_boxcall_simple_reads) Removed; logic consolidated in core.rs length/charCodeAt handlers.
|
||||
|
||||
// Map.get(key): handle I64 and HH variants with registry check and events
|
||||
pub fn lower_map_get(
|
||||
func: &MirFunction,
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
// Build observed arg kinds using TyEnv when available
|
||||
let mut observed_kinds: Vec<crate::jit::hostcall_registry::ArgKind> = Vec::new();
|
||||
observed_kinds.push(crate::jit::hostcall_registry::ArgKind::Handle); // receiver
|
||||
let key_kind = if let Some(key_vid) = args.get(0) {
|
||||
if let Some(mt) = func.metadata.value_types.get(key_vid) {
|
||||
match mt {
|
||||
crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::I64, // coerced via VM path
|
||||
crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64,
|
||||
crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64,
|
||||
crate::mir::MirType::String | crate::mir::MirType::Box(_) => {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
}
|
||||
_ => {
|
||||
if let Some(_) = args.get(0).and_then(|v| known_i64.get(v)) {
|
||||
crate::jit::hostcall_registry::ArgKind::I64
|
||||
} else {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(_) = args.get(0).and_then(|v| known_i64.get(v)) {
|
||||
crate::jit::hostcall_registry::ArgKind::I64
|
||||
} else {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
}
|
||||
} else {
|
||||
crate::jit::hostcall_registry::ArgKind::I64
|
||||
};
|
||||
observed_kinds.push(key_kind);
|
||||
|
||||
let arg_types: Vec<&'static str> = observed_kinds
|
||||
.iter()
|
||||
.map(|k| match k {
|
||||
crate::jit::hostcall_registry::ArgKind::I64 => "I64",
|
||||
crate::jit::hostcall_registry::ArgKind::F64 => "F64",
|
||||
crate::jit::hostcall_registry::ArgKind::Handle => "Handle",
|
||||
})
|
||||
.collect();
|
||||
let canonical = "nyash.map.get_h";
|
||||
match crate::jit::hostcall_registry::check_signature(canonical, &observed_kinds) {
|
||||
Ok(()) => {
|
||||
let event_id = if matches!(key_kind, crate::jit::hostcall_registry::ArgKind::Handle)
|
||||
&& args.get(0).and_then(|v| param_index.get(v)).is_some()
|
||||
{
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_HH
|
||||
} else {
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_H
|
||||
};
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": event_id,
|
||||
"decision": "allow",
|
||||
"reason": "sig_ok",
|
||||
"argc": observed_kinds.len(),
|
||||
"arg_types": arg_types
|
||||
}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
if matches!(key_kind, crate::jit::hostcall_registry::ArgKind::I64) {
|
||||
let key_i = args
|
||||
.get(0)
|
||||
.and_then(|v| known_i64.get(v))
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_const_i64(key_i);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_H,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
} else if let Some(kp) = args.get(0).and_then(|v| param_index.get(v)).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_param_i64(kp);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_MAP_GET_HH,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
} else {
|
||||
// Not a param: fall back (receiver_not_param or key_not_param already logged)
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": canonical,
|
||||
"decision": "fallback",
|
||||
"reason": reason,
|
||||
"argc": observed_kinds.len(),
|
||||
"arg_types": arg_types
|
||||
}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// receiver not a param; emit info and fallback
|
||||
let mut observed_kinds: Vec<crate::jit::hostcall_registry::ArgKind> = Vec::new();
|
||||
observed_kinds.push(crate::jit::hostcall_registry::ArgKind::Handle);
|
||||
let key_kind = if let Some(key_vid) = args.get(0) {
|
||||
if let Some(mt) = func.metadata.value_types.get(key_vid) {
|
||||
match mt {
|
||||
crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64,
|
||||
crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::I64,
|
||||
crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64,
|
||||
crate::mir::MirType::String | crate::mir::MirType::Box(_) => {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
}
|
||||
_ => crate::jit::hostcall_registry::ArgKind::Handle,
|
||||
}
|
||||
} else {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
}
|
||||
} else {
|
||||
crate::jit::hostcall_registry::ArgKind::Handle
|
||||
};
|
||||
observed_kinds.push(key_kind);
|
||||
let arg_types: Vec<&'static str> = observed_kinds
|
||||
.iter()
|
||||
.map(|k| match k {
|
||||
crate::jit::hostcall_registry::ArgKind::I64 => "I64",
|
||||
crate::jit::hostcall_registry::ArgKind::F64 => "F64",
|
||||
crate::jit::hostcall_registry::ArgKind::Handle => "Handle",
|
||||
})
|
||||
.collect();
|
||||
let sym = "nyash.map.get_h";
|
||||
let decision = match crate::jit::hostcall_registry::check_signature(sym, &observed_kinds) {
|
||||
Ok(()) => ("fallback", "receiver_not_param"),
|
||||
Err(reason) => ("fallback", reason),
|
||||
};
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": sym,
|
||||
"decision": decision.0,
|
||||
"reason": decision.1,
|
||||
"argc": observed_kinds.len(),
|
||||
"arg_types": arg_types
|
||||
}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_has(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
let key = args
|
||||
.get(0)
|
||||
.and_then(|v| known_i64.get(v))
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_const_i64(key);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_MAP_HAS_H,
|
||||
2,
|
||||
dst.is_some(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// math.*: decide allow/fallback via registry; on allow + native_f64, emit typed hostcall
|
||||
pub fn lower_math_call(
|
||||
func: &MirFunction,
|
||||
b: &mut dyn IRBuilder,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
known_f64: &HashMap<ValueId, f64>,
|
||||
float_box_values: &std::collections::HashSet<ValueId>,
|
||||
method: &str,
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) {
|
||||
use crate::jit::hostcall_registry::{check_signature, ArgKind};
|
||||
let sym = format!("nyash.math.{}", method);
|
||||
|
||||
// Build observed kinds using TyEnv when available; fallback to known maps / FloatBox tracking
|
||||
let mut observed_kinds: Vec<ArgKind> = Vec::new();
|
||||
for v in args.iter() {
|
||||
let kind = if let Some(mt) = func.metadata.value_types.get(v) {
|
||||
match mt {
|
||||
crate::mir::MirType::Float => ArgKind::F64,
|
||||
crate::mir::MirType::Integer => ArgKind::I64,
|
||||
crate::mir::MirType::Bool => ArgKind::I64,
|
||||
crate::mir::MirType::String | crate::mir::MirType::Box(_) => ArgKind::Handle,
|
||||
_ => {
|
||||
if known_f64.contains_key(v) || float_box_values.contains(v) {
|
||||
ArgKind::F64
|
||||
} else {
|
||||
ArgKind::I64
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if known_f64.contains_key(v) || float_box_values.contains(v) {
|
||||
ArgKind::F64
|
||||
} else {
|
||||
ArgKind::I64
|
||||
}
|
||||
};
|
||||
observed_kinds.push(kind);
|
||||
}
|
||||
let arg_types: Vec<&'static str> = observed_kinds
|
||||
.iter()
|
||||
.map(|k| match k {
|
||||
ArgKind::I64 => "I64",
|
||||
ArgKind::F64 => "F64",
|
||||
ArgKind::Handle => "Handle",
|
||||
})
|
||||
.collect();
|
||||
|
||||
match check_signature(&sym, &observed_kinds) {
|
||||
Ok(()) => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"allow", "reason":"sig_ok", "argc": observed_kinds.len(), "arg_types": arg_types}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
if crate::jit::config::current().native_f64 {
|
||||
let (symbol, arity) = match method {
|
||||
"sin" => ("nyash.math.sin_f64", 1),
|
||||
"cos" => ("nyash.math.cos_f64", 1),
|
||||
"abs" => ("nyash.math.abs_f64", 1),
|
||||
"min" => ("nyash.math.min_f64", 2),
|
||||
"max" => ("nyash.math.max_f64", 2),
|
||||
_ => ("nyash.math.sin_f64", 1),
|
||||
};
|
||||
for i in 0..arity {
|
||||
if let Some(v) = args.get(i) {
|
||||
if let Some(fv) = known_f64.get(v).copied() {
|
||||
b.emit_const_f64(fv);
|
||||
continue;
|
||||
}
|
||||
if let Some(iv) = known_i64.get(v).copied() {
|
||||
b.emit_const_f64(iv as f64);
|
||||
continue;
|
||||
}
|
||||
let mut emitted = false;
|
||||
'scan: for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type,
|
||||
args: nb_args,
|
||||
} = ins
|
||||
{
|
||||
if *dst == *v && box_type == "FloatBox" {
|
||||
if let Some(srcv) = nb_args.get(0) {
|
||||
if let Some(fv) = known_f64.get(srcv).copied() {
|
||||
b.emit_const_f64(fv);
|
||||
emitted = true;
|
||||
break 'scan;
|
||||
}
|
||||
if let Some(iv) = known_i64.get(srcv).copied() {
|
||||
b.emit_const_f64(iv as f64);
|
||||
emitted = true;
|
||||
break 'scan;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !emitted {
|
||||
b.emit_const_f64(0.0);
|
||||
}
|
||||
} else {
|
||||
b.emit_const_f64(0.0);
|
||||
}
|
||||
}
|
||||
let kinds: Vec<super::builder::ParamKind> =
|
||||
(0..arity).map(|_| super::builder::ParamKind::F64).collect();
|
||||
b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true);
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason": reason, "argc": observed_kinds.len(), "arg_types": arg_types}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
306
archive/jit-cranelift/src/jit/lower/core_ops.rs
Normal file
306
archive/jit-cranelift/src/jit/lower/core_ops.rs
Normal file
@ -0,0 +1,306 @@
|
||||
//! Core ops lowering (non-hostcall): BinOp, Compare, Branch, Jump
|
||||
use super::builder::{BinOpKind, CmpKind, IRBuilder};
|
||||
use crate::mir::{BinaryOp, CompareOp, MirFunction, MirType, ValueId};
|
||||
|
||||
use super::core::LowerCore;
|
||||
|
||||
impl LowerCore {
|
||||
fn is_string_like(&self, func: &MirFunction, v: &ValueId) -> bool {
|
||||
// Check per-value type metadata
|
||||
if let Some(mt) = func.metadata.value_types.get(v) {
|
||||
if matches!(mt, MirType::String) {
|
||||
return true;
|
||||
}
|
||||
if let MirType::Box(ref name) = mt {
|
||||
if name == "StringBox" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if this value is a parameter with String or StringBox type
|
||||
if let Some(pidx) = self.param_index.get(v).copied() {
|
||||
if let Some(pt) = func.signature.params.get(pidx) {
|
||||
if matches!(pt, MirType::String) {
|
||||
return true;
|
||||
}
|
||||
if let MirType::Box(ref name) = pt {
|
||||
if name == "StringBox" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if it originates from a StringBox NewBox
|
||||
if let Some(name) = self.box_type_map.get(v) {
|
||||
if name == "StringBox" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn lower_binop(
|
||||
&mut self,
|
||||
b: &mut dyn IRBuilder,
|
||||
op: &BinaryOp,
|
||||
lhs: &ValueId,
|
||||
rhs: &ValueId,
|
||||
dst: &ValueId,
|
||||
func: &MirFunction,
|
||||
) {
|
||||
// Optional: consult unified grammar for operator strategy (non-invasive logging)
|
||||
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
|
||||
match op {
|
||||
BinaryOp::Add => {
|
||||
let strat = crate::grammar::engine::get().add_coercion_strategy();
|
||||
crate::jit::events::emit(
|
||||
"grammar",
|
||||
"add",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({"coercion": strat}),
|
||||
);
|
||||
}
|
||||
BinaryOp::Sub => {
|
||||
let strat = crate::grammar::engine::get().sub_coercion_strategy();
|
||||
crate::jit::events::emit(
|
||||
"grammar",
|
||||
"sub",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({"coercion": strat}),
|
||||
);
|
||||
}
|
||||
BinaryOp::Mul => {
|
||||
let strat = crate::grammar::engine::get().mul_coercion_strategy();
|
||||
crate::jit::events::emit(
|
||||
"grammar",
|
||||
"mul",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({"coercion": strat}),
|
||||
);
|
||||
}
|
||||
BinaryOp::Div => {
|
||||
let strat = crate::grammar::engine::get().div_coercion_strategy();
|
||||
crate::jit::events::emit(
|
||||
"grammar",
|
||||
"div",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({"coercion": strat}),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Route string-like addition to hostcall (handle,handle)
|
||||
if crate::jit::config::current().hostcall {
|
||||
if matches!(op, BinaryOp::Add) {
|
||||
if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) {
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_STRING_CONCAT_HH,
|
||||
2,
|
||||
true,
|
||||
);
|
||||
// Track handle result for downstream usages
|
||||
self.handle_values.insert(*dst);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| {
|
||||
let id = self.next_local;
|
||||
self.next_local += 1;
|
||||
id
|
||||
});
|
||||
b.store_local_i64(slot);
|
||||
return;
|
||||
}
|
||||
// If dynamic Box/Unknown types, route to unified semantics add (handle,handle)
|
||||
let is_dynamic = match (
|
||||
func.metadata.value_types.get(lhs),
|
||||
func.metadata.value_types.get(rhs),
|
||||
) {
|
||||
(Some(MirType::Box(_)) | Some(MirType::Unknown) | None, _)
|
||||
| (_, Some(MirType::Box(_)) | Some(MirType::Unknown) | None) => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_dynamic {
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
b.emit_host_call(
|
||||
crate::jit::r#extern::collections::SYM_SEMANTICS_ADD_HH,
|
||||
2,
|
||||
true,
|
||||
);
|
||||
self.handle_values.insert(*dst);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| {
|
||||
let id = self.next_local;
|
||||
self.next_local += 1;
|
||||
id
|
||||
});
|
||||
b.store_local_i64(slot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let kind = match op {
|
||||
BinaryOp::Add => BinOpKind::Add,
|
||||
BinaryOp::Sub => BinOpKind::Sub,
|
||||
BinaryOp::Mul => BinOpKind::Mul,
|
||||
BinaryOp::Div => BinOpKind::Div,
|
||||
BinaryOp::Mod => BinOpKind::Mod,
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
b.emit_binop(kind);
|
||||
if let (Some(a), Some(bv)) = (self.known_i64.get(lhs), self.known_i64.get(rhs)) {
|
||||
let res = match op {
|
||||
BinaryOp::Add => a.wrapping_add(*bv),
|
||||
BinaryOp::Sub => a.wrapping_sub(*bv),
|
||||
BinaryOp::Mul => a.wrapping_mul(*bv),
|
||||
BinaryOp::Div => {
|
||||
if *bv != 0 {
|
||||
a.wrapping_div(*bv)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
BinaryOp::Mod => {
|
||||
if *bv != 0 {
|
||||
a.wrapping_rem(*bv)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
self.known_i64.insert(*dst, res);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_compare(
|
||||
&mut self,
|
||||
b: &mut dyn IRBuilder,
|
||||
op: &CompareOp,
|
||||
lhs: &ValueId,
|
||||
rhs: &ValueId,
|
||||
dst: &ValueId,
|
||||
func: &MirFunction,
|
||||
) {
|
||||
// Route string-like comparisons (Eq/Lt) to hostcalls (i64 0/1)
|
||||
if crate::jit::config::current().hostcall {
|
||||
if matches!(op, CompareOp::Eq | CompareOp::Lt) {
|
||||
if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) {
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let sym = match op {
|
||||
CompareOp::Eq => crate::jit::r#extern::collections::SYM_STRING_EQ_HH,
|
||||
CompareOp::Lt => crate::jit::r#extern::collections::SYM_STRING_LT_HH,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
b.emit_host_call(sym, 2, true);
|
||||
self.bool_values.insert(*dst);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let kind = match op {
|
||||
CompareOp::Eq => CmpKind::Eq,
|
||||
CompareOp::Ne => CmpKind::Ne,
|
||||
CompareOp::Lt => CmpKind::Lt,
|
||||
CompareOp::Le => CmpKind::Le,
|
||||
CompareOp::Gt => CmpKind::Gt,
|
||||
CompareOp::Ge => CmpKind::Ge,
|
||||
};
|
||||
b.emit_compare(kind);
|
||||
// Persist compare result in a local slot so terminators (Branch) can reload it reliably
|
||||
self.bool_values.insert(*dst);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| {
|
||||
let id = self.next_local;
|
||||
self.next_local += 1;
|
||||
id
|
||||
});
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
|
||||
pub fn lower_jump(&mut self, b: &mut dyn IRBuilder) {
|
||||
b.emit_jump();
|
||||
}
|
||||
pub fn lower_branch(&mut self, b: &mut dyn IRBuilder) {
|
||||
b.emit_branch();
|
||||
}
|
||||
}
|
||||
|
||||
// Methods moved from core.rs to reduce file size and centralize op helpers
|
||||
impl LowerCore {
|
||||
// Push a value if known or param/local/phi
|
||||
pub(super) fn push_value_if_known_or_param(&self, b: &mut dyn IRBuilder, id: &ValueId) {
|
||||
// Prefer compile-time known constants to avoid stale local slots overshadowing folded values
|
||||
if let Some(v) = self.known_i64.get(id).copied() {
|
||||
b.emit_const_i64(v);
|
||||
return;
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(id).copied() {
|
||||
b.load_local_i64(slot);
|
||||
return;
|
||||
}
|
||||
if self.phi_values.contains(id) {
|
||||
let pos = self
|
||||
.phi_param_index
|
||||
.iter()
|
||||
.find_map(|((_, vid), idx)| if vid == id { Some(*idx) } else { None })
|
||||
.unwrap_or(0);
|
||||
if crate::jit::config::current().native_bool && self.bool_phi_values.contains(id) {
|
||||
b.push_block_param_b1_at(pos);
|
||||
} else {
|
||||
b.push_block_param_i64_at(pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Some(pidx) = self.param_index.get(id).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Coverage helper: increments covered/unsupported counts
|
||||
pub(super) fn cover_if_supported(&mut self, instr: &crate::mir::MirInstruction) {
|
||||
use crate::mir::MirInstruction as I;
|
||||
let supported = matches!(
|
||||
instr,
|
||||
I::Const { .. }
|
||||
| I::Copy { .. }
|
||||
| I::Cast { .. }
|
||||
| I::TypeCheck { .. }
|
||||
| I::TypeOp { .. }
|
||||
| I::BinOp { .. }
|
||||
| I::Compare { .. }
|
||||
| I::Jump { .. }
|
||||
| I::Branch { .. }
|
||||
| I::Return { .. }
|
||||
| I::Call { .. }
|
||||
| I::BoxCall { .. }
|
||||
| I::ArrayGet { .. }
|
||||
| I::ArraySet { .. }
|
||||
| I::NewBox { .. }
|
||||
| I::Store { .. }
|
||||
| I::Load { .. }
|
||||
| I::Phi { .. }
|
||||
| I::Debug { .. }
|
||||
| I::ExternCall { .. }
|
||||
| I::Safepoint
|
||||
| I::Nop
|
||||
| I::PluginInvoke { .. }
|
||||
);
|
||||
if supported {
|
||||
self.covered += 1;
|
||||
} else {
|
||||
self.unsupported += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
1520
archive/jit-cranelift/src/jit/lower/extern_thunks.rs
Normal file
1520
archive/jit-cranelift/src/jit/lower/extern_thunks.rs
Normal file
File diff suppressed because it is too large
Load Diff
7
archive/jit-cranelift/src/jit/lower/mod.rs
Normal file
7
archive/jit-cranelift/src/jit/lower/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Lowering entry for JIT
|
||||
pub mod builder;
|
||||
pub mod cfg_dot;
|
||||
pub mod core;
|
||||
pub mod core_hostcall;
|
||||
pub mod core_ops;
|
||||
pub mod extern_thunks;
|
||||
319
archive/jit-cranelift/src/jit/manager.rs
Normal file
319
archive/jit-cranelift/src/jit/manager.rs
Normal file
@ -0,0 +1,319 @@
|
||||
#[cfg(feature = "jit-direct-only")]
|
||||
pub struct JitManager;
|
||||
|
||||
#[cfg(feature = "jit-direct-only")]
|
||||
impl JitManager {
|
||||
pub fn new(_threshold: u32) -> Self {
|
||||
Self
|
||||
}
|
||||
pub fn set_threshold(&mut self, _t: u32) {}
|
||||
pub fn record_entry(&mut self, _func: &str) {}
|
||||
pub fn should_jit(&self, _func: &str) -> bool {
|
||||
false
|
||||
}
|
||||
pub fn mark_compiled(&mut self, _func: &str, _handle: u64) {}
|
||||
pub fn maybe_compile(&mut self, _func: &str, _mir: &crate::mir::MirFunction) -> bool {
|
||||
false
|
||||
}
|
||||
pub fn is_compiled(&self, _func: &str) -> bool {
|
||||
false
|
||||
}
|
||||
pub fn handle_of(&self, _func: &str) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
pub fn sites(&self) -> usize {
|
||||
0
|
||||
}
|
||||
pub fn compiled_count(&self) -> usize {
|
||||
0
|
||||
}
|
||||
pub fn total_hits(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
pub fn exec_ok_count(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
pub fn exec_trap_count(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
pub fn record_lower_stats(
|
||||
&mut self,
|
||||
_func: &str,
|
||||
_phi_total: u64,
|
||||
_phi_b1: u64,
|
||||
_ret_bool_hint: bool,
|
||||
) {
|
||||
}
|
||||
pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> {
|
||||
Vec::new()
|
||||
}
|
||||
pub fn top_hits(&self, _n: usize) -> Vec<(String, u32, bool, u64)> {
|
||||
Vec::new()
|
||||
}
|
||||
pub fn print_summary(&self) {}
|
||||
pub fn maybe_dispatch(&mut self, _func: &str, _argc: usize) -> bool {
|
||||
false
|
||||
}
|
||||
pub fn execute_compiled(
|
||||
&mut self,
|
||||
_func: &str,
|
||||
_ret_ty: &crate::mir::MirType,
|
||||
_args: &[crate::backend::vm::VMValue],
|
||||
) -> Option<crate::backend::vm::VMValue> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Minimal JIT manager skeleton for Phase 10_a
|
||||
/// - Tracks per-function entry counts
|
||||
/// - Decides when a function should be JIT-compiled (threshold)
|
||||
/// - Records compiled functions for stats
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
pub struct JitManager {
|
||||
threshold: u32,
|
||||
hits: HashMap<String, u32>,
|
||||
compiled: HashMap<String, u64>,
|
||||
engine: crate::jit::engine::JitEngine,
|
||||
exec_ok: u64,
|
||||
exec_trap: u64,
|
||||
// Per-function lowering stats (accumulated)
|
||||
func_phi_total: HashMap<String, u64>,
|
||||
func_phi_b1: HashMap<String, u64>,
|
||||
func_ret_bool_hint: HashMap<String, u64>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
impl JitManager {
|
||||
pub fn new(threshold: u32) -> Self {
|
||||
Self {
|
||||
threshold,
|
||||
hits: HashMap::new(),
|
||||
compiled: HashMap::new(),
|
||||
engine: crate::jit::engine::JitEngine::new(),
|
||||
exec_ok: 0,
|
||||
exec_trap: 0,
|
||||
func_phi_total: HashMap::new(),
|
||||
func_phi_b1: HashMap::new(),
|
||||
func_ret_bool_hint: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_threshold(&mut self, t: u32) {
|
||||
self.threshold = t.max(1);
|
||||
}
|
||||
|
||||
pub fn record_entry(&mut self, func: &str) {
|
||||
let c = self.hits.entry(func.to_string()).or_insert(0);
|
||||
*c = c.saturating_add(1);
|
||||
}
|
||||
|
||||
pub fn should_jit(&self, func: &str) -> bool {
|
||||
let hot = self.hits.get(func).copied().unwrap_or(0) >= self.threshold;
|
||||
hot && !self.compiled.contains_key(func)
|
||||
}
|
||||
|
||||
pub fn mark_compiled(&mut self, func: &str, handle: u64) {
|
||||
self.compiled.insert(func.to_string(), handle);
|
||||
}
|
||||
|
||||
/// Ensure the function is compiled when hot; returns true if compiled now or already compiled
|
||||
pub fn maybe_compile(&mut self, func: &str, mir: &crate::mir::MirFunction) -> bool {
|
||||
if self.should_jit(func) {
|
||||
if let Some(handle) = self.engine.compile_function(func, mir) {
|
||||
self.mark_compiled(func, handle);
|
||||
// Record per-function lower stats captured by engine
|
||||
let (phi_t, phi_b1, ret_b) = self.engine.last_lower_stats();
|
||||
self.record_lower_stats(func, phi_t, phi_b1, ret_b);
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
|
||||
eprintln!("[JIT] compiled {} -> handle={}", func, handle);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
self.compiled.contains_key(func)
|
||||
}
|
||||
|
||||
pub fn is_compiled(&self, func: &str) -> bool {
|
||||
self.compiled.contains_key(func)
|
||||
}
|
||||
pub fn handle_of(&self, func: &str) -> Option<u64> {
|
||||
self.compiled.get(func).copied()
|
||||
}
|
||||
|
||||
// --- Stats accessors for unified reporting ---
|
||||
pub fn sites(&self) -> usize {
|
||||
self.hits.len()
|
||||
}
|
||||
pub fn compiled_count(&self) -> usize {
|
||||
self.compiled.len()
|
||||
}
|
||||
pub fn total_hits(&self) -> u64 {
|
||||
self.hits.values().map(|v| *v as u64).sum()
|
||||
}
|
||||
pub fn exec_ok_count(&self) -> u64 {
|
||||
self.exec_ok
|
||||
}
|
||||
pub fn exec_trap_count(&self) -> u64 {
|
||||
self.exec_trap
|
||||
}
|
||||
|
||||
// --- Per-function stats ---
|
||||
pub fn record_lower_stats(
|
||||
&mut self,
|
||||
func: &str,
|
||||
phi_total: u64,
|
||||
phi_b1: u64,
|
||||
ret_bool_hint: bool,
|
||||
) {
|
||||
if phi_total > 0 {
|
||||
*self.func_phi_total.entry(func.to_string()).or_insert(0) += phi_total;
|
||||
}
|
||||
if phi_b1 > 0 {
|
||||
*self.func_phi_b1.entry(func.to_string()).or_insert(0) += phi_b1;
|
||||
}
|
||||
if ret_bool_hint {
|
||||
*self.func_ret_bool_hint.entry(func.to_string()).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> {
|
||||
// name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle
|
||||
let mut names: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
|
||||
names.extend(self.hits.keys().cloned());
|
||||
names.extend(self.func_phi_total.keys().cloned());
|
||||
names.extend(self.func_phi_b1.keys().cloned());
|
||||
names.extend(self.func_ret_bool_hint.keys().cloned());
|
||||
let mut out = Vec::new();
|
||||
for name in names {
|
||||
let phi_t = self.func_phi_total.get(&name).copied().unwrap_or(0);
|
||||
let phi_b1 = self.func_phi_b1.get(&name).copied().unwrap_or(0);
|
||||
let rb = self.func_ret_bool_hint.get(&name).copied().unwrap_or(0);
|
||||
let hits = self.hits.get(&name).copied().unwrap_or(0);
|
||||
let compiled = self.compiled.contains_key(&name);
|
||||
let handle = self.compiled.get(&name).copied().unwrap_or(0);
|
||||
out.push((name, phi_t, phi_b1, rb, hits, compiled, handle));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Return top-N hot functions by hits, with compiled flag and handle
|
||||
pub fn top_hits(&self, n: usize) -> Vec<(String, u32, bool, u64)> {
|
||||
let mut v: Vec<(&String, &u32)> = self.hits.iter().collect();
|
||||
v.sort_by(|a, b| b.1.cmp(a.1));
|
||||
v.into_iter()
|
||||
.take(n)
|
||||
.map(|(k, h)| {
|
||||
let compiled = self.compiled.contains_key(k);
|
||||
let handle = self.compiled.get(k).copied().unwrap_or(0);
|
||||
(k.clone(), *h, compiled, handle)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn print_summary(&self) {
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() != Some("1") {
|
||||
return;
|
||||
}
|
||||
let sites = self.hits.len();
|
||||
let total_hits: u64 = self.hits.values().map(|v| *v as u64).sum();
|
||||
let compiled = self.compiled.len();
|
||||
eprintln!(
|
||||
"[JIT] sites={} compiled={} hits_total={} exec_ok={} exec_trap={}",
|
||||
sites, compiled, total_hits, self.exec_ok, self.exec_trap
|
||||
);
|
||||
// Top 5 hot functions
|
||||
let mut v: Vec<(&String, &u32)> = self.hits.iter().collect();
|
||||
v.sort_by(|a, b| b.1.cmp(a.1));
|
||||
for (i, (k, h)) in v.into_iter().take(5).enumerate() {
|
||||
let comp = if self.compiled.contains_key(k) {
|
||||
"*"
|
||||
} else {
|
||||
" "
|
||||
};
|
||||
let hdl = self.compiled.get(k).copied().unwrap_or(0);
|
||||
eprintln!(" #{}{} {} hits={} handle={}", i + 1, comp, k, h, hdl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 10_c stub: attempt to dispatch to JIT if enabled; returns true if it would execute
|
||||
pub fn maybe_dispatch(&mut self, func: &str, argc: usize) -> bool {
|
||||
if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") {
|
||||
if let Some(h) = self.handle_of(func) {
|
||||
eprintln!(
|
||||
"[JIT] executing handle={} argc={} (stub) for {}",
|
||||
h, argc, func
|
||||
);
|
||||
// In 10_c proper, invoke engine with prepared args and return actual result
|
||||
// For now, execute with empty args to exercise the path, ignore result
|
||||
let _ = self.engine.execute_handle(h, &[]);
|
||||
return false; // keep VM path active for now
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken.
|
||||
pub fn execute_compiled(
|
||||
&mut self,
|
||||
func: &str,
|
||||
ret_ty: &crate::mir::MirType,
|
||||
args: &[crate::backend::vm::VMValue],
|
||||
) -> Option<crate::backend::vm::VMValue> {
|
||||
// Strict/Fail‑FastモードではJITは"コンパイル専用"(実行しない)
|
||||
if std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") {
|
||||
// 観測のためイベントだけ出す
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({
|
||||
"id": "jit_skip_execute_strict",
|
||||
"func": func
|
||||
}),
|
||||
"jit",
|
||||
func,
|
||||
);
|
||||
return None;
|
||||
}
|
||||
if let Some(h) = self.handle_of(func) {
|
||||
// Expose args to both legacy VM hostcalls and new JIT ABI TLS
|
||||
crate::jit::rt::set_legacy_vm_args(args);
|
||||
let jit_args = crate::jit::abi::adapter::to_jit_values(args);
|
||||
crate::jit::rt::set_current_jit_args(&jit_args);
|
||||
let t0 = std::time::Instant::now();
|
||||
// Begin handle scope so temporary handles are reclaimed after the call
|
||||
crate::jit::rt::handles::begin_scope();
|
||||
let out = self.engine.execute_handle(h, &jit_args);
|
||||
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
|
||||
let dt = t0.elapsed();
|
||||
eprintln!("[JIT] exec_time_ms={} for {}", dt.as_millis(), func);
|
||||
}
|
||||
let res = match out {
|
||||
Some(v) => {
|
||||
self.exec_ok = self.exec_ok.saturating_add(1);
|
||||
// Use CallBoundaryBox to convert JitValue → VMValue with MIR ret type hint
|
||||
let vmv = crate::jit::boundary::CallBoundaryBox::to_vm(ret_ty, v);
|
||||
Some(vmv)
|
||||
}
|
||||
None => {
|
||||
self.exec_trap = self.exec_trap.saturating_add(1);
|
||||
// Emit a minimal trap event for observability (runtime only)
|
||||
let dt = t0.elapsed();
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({
|
||||
"kind": "trap", // redundant with wrapper kind but explicit here for clarity
|
||||
"reason": "jit_execute_failed",
|
||||
"ms": dt.as_millis()
|
||||
}),
|
||||
"trap",
|
||||
func,
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
// Clear handles created during this call
|
||||
crate::jit::rt::handles::end_scope_clear();
|
||||
return res;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
16
archive/jit-cranelift/src/jit/mod.rs
Normal file
16
archive/jit-cranelift/src/jit/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//! JIT subsystem: Cranelift-based JIT manager and lowering stubs
|
||||
|
||||
pub mod abi;
|
||||
pub mod boundary;
|
||||
pub mod config;
|
||||
pub mod engine;
|
||||
pub mod events;
|
||||
pub mod r#extern;
|
||||
pub mod hostcall_registry;
|
||||
pub mod lower;
|
||||
pub mod manager;
|
||||
pub mod observe;
|
||||
pub mod policy;
|
||||
pub mod rt;
|
||||
pub mod semantics;
|
||||
pub mod shim_trace;
|
||||
55
archive/jit-cranelift/src/jit/observe.rs
Normal file
55
archive/jit-cranelift/src/jit/observe.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! Observe facade: centralize compile/runtime/trace output rules.
|
||||
//! Thin wrappers around jit::events and shim_trace to keep callsites tidy.
|
||||
|
||||
pub fn lower_plugin_invoke(
|
||||
box_type: &str,
|
||||
method: &str,
|
||||
type_id: u32,
|
||||
method_id: u32,
|
||||
argc: usize,
|
||||
) {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": format!("plugin:{}:{}", box_type, method),
|
||||
"decision":"allow","reason":"plugin_invoke","argc": argc,
|
||||
"type_id": type_id, "method_id": method_id
|
||||
}),
|
||||
"plugin",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
|
||||
pub fn lower_hostcall(symbol: &str, argc: usize, arg_types: &[&str], decision: &str, reason: &str) {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({
|
||||
"id": symbol,
|
||||
"decision": decision,
|
||||
"reason": reason,
|
||||
"argc": argc,
|
||||
"arg_types": arg_types
|
||||
}),
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
|
||||
pub fn runtime_plugin_shim_i64(type_id: i64, method_id: i64, argc: i64, inst: u32) {
|
||||
crate::jit::events::emit_runtime(
|
||||
serde_json::json!({
|
||||
"id": "plugin_invoke.i64",
|
||||
"type_id": type_id,
|
||||
"method_id": method_id,
|
||||
"argc": argc,
|
||||
"inst": inst
|
||||
}),
|
||||
"plugin",
|
||||
"<jit>",
|
||||
);
|
||||
}
|
||||
|
||||
pub fn trace_push(msg: String) {
|
||||
crate::jit::shim_trace::push(msg);
|
||||
}
|
||||
pub fn trace_enabled() -> bool {
|
||||
crate::jit::shim_trace::is_enabled()
|
||||
}
|
||||
58
archive/jit-cranelift/src/jit/policy.rs
Normal file
58
archive/jit-cranelift/src/jit/policy.rs
Normal file
@ -0,0 +1,58 @@
|
||||
//! JIT Policy (Box-First): centralizes runtime decisions
|
||||
//!
|
||||
//! Minimal v0:
|
||||
//! - read_only: if true, deny write-effects in jit-direct and other independent paths
|
||||
//! - hostcall_whitelist: symbolic names allowed (future use)
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct JitPolicy {
|
||||
pub read_only: bool,
|
||||
pub hostcall_whitelist: Vec<String>,
|
||||
}
|
||||
|
||||
impl JitPolicy {
|
||||
pub fn from_env() -> Self {
|
||||
let ro = std::env::var("NYASH_JIT_READ_ONLY").ok().as_deref() == Some("1");
|
||||
// Comma-separated hostcall names
|
||||
let hc = std::env::var("NYASH_JIT_HOSTCALL_WHITELIST")
|
||||
.ok()
|
||||
.map(|s| {
|
||||
s.split(',')
|
||||
.map(|t| t.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Self {
|
||||
read_only: ro,
|
||||
hostcall_whitelist: hc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GLOBAL: OnceCell<RwLock<JitPolicy>> = OnceCell::new();
|
||||
|
||||
pub fn current() -> JitPolicy {
|
||||
if let Some(l) = GLOBAL.get() {
|
||||
if let Ok(g) = l.read() {
|
||||
return g.clone();
|
||||
}
|
||||
}
|
||||
JitPolicy::from_env()
|
||||
}
|
||||
|
||||
pub fn set_current(p: JitPolicy) {
|
||||
if let Some(l) = GLOBAL.get() {
|
||||
if let Ok(mut w) = l.write() {
|
||||
*w = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
let _ = GLOBAL.set(RwLock::new(p));
|
||||
}
|
||||
|
||||
// Submodule: invoke decision policy
|
||||
pub mod invoke;
|
||||
90
archive/jit-cranelift/src/jit/policy/invoke.rs
Normal file
90
archive/jit-cranelift/src/jit/policy/invoke.rs
Normal file
@ -0,0 +1,90 @@
|
||||
//! InvokePolicyPass (minimal scaffold)
|
||||
//! Centralizes decision for plugin/hostcall to keep lowerer slim.
|
||||
//! HostCall優先(Core-13方針)。ENV `NYASH_USE_PLUGIN_BUILTINS=1` の場合のみ
|
||||
//! plugin_invoke を試し、解決できない場合はHostCallへフォールバックする。
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InvokeDecision {
|
||||
PluginInvoke {
|
||||
type_id: u32,
|
||||
method_id: u32,
|
||||
box_type: String,
|
||||
method: String,
|
||||
argc: usize,
|
||||
has_ret: bool,
|
||||
},
|
||||
HostCall {
|
||||
symbol: String,
|
||||
argc: usize,
|
||||
has_ret: bool,
|
||||
reason: &'static str,
|
||||
},
|
||||
Fallback {
|
||||
reason: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
fn use_plugin_builtins() -> bool {
|
||||
#[cfg(feature = "jit-direct-only")]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
{
|
||||
return std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1");
|
||||
}
|
||||
}
|
||||
|
||||
/// Decide invocation policy for a known Box method.
|
||||
pub fn decide_box_method(
|
||||
box_type: &str,
|
||||
method: &str,
|
||||
argc: usize,
|
||||
has_ret: bool,
|
||||
) -> InvokeDecision {
|
||||
// HostCall mapping for common collections/strings/instance ops
|
||||
let symbol = match (box_type, method) {
|
||||
("ArrayBox", "length") => crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
("StringBox", "length") | ("StringBox", "len") => "nyash.string.len_h",
|
||||
("ArrayBox", "get") => crate::jit::r#extern::collections::SYM_ARRAY_GET_H,
|
||||
("ArrayBox", "set") => crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
|
||||
("ArrayBox", "push") => crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H,
|
||||
("MapBox", "size") => crate::jit::r#extern::collections::SYM_MAP_SIZE_H,
|
||||
("MapBox", "get") => crate::jit::r#extern::collections::SYM_MAP_GET_HH,
|
||||
("MapBox", "has") => crate::jit::r#extern::collections::SYM_MAP_HAS_H,
|
||||
("MapBox", "set") => crate::jit::r#extern::collections::SYM_MAP_SET_H,
|
||||
("StringBox", "is_empty") => crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H,
|
||||
("StringBox", "charCodeAt") => crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H,
|
||||
_ => "", // unknown
|
||||
};
|
||||
// Prefer HostCall when available
|
||||
if !symbol.is_empty() {
|
||||
InvokeDecision::HostCall {
|
||||
symbol: symbol.to_string(),
|
||||
argc,
|
||||
has_ret,
|
||||
reason: "mapped_symbol",
|
||||
}
|
||||
} else if use_plugin_builtins() {
|
||||
// Try plugin_invoke as a secondary path when enabled
|
||||
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
if let Ok(h) = ph.resolve_method(box_type, method) {
|
||||
return InvokeDecision::PluginInvoke {
|
||||
type_id: h.type_id,
|
||||
method_id: h.method_id,
|
||||
box_type: h.box_type,
|
||||
method: method.to_string(),
|
||||
argc,
|
||||
has_ret,
|
||||
};
|
||||
}
|
||||
}
|
||||
InvokeDecision::Fallback {
|
||||
reason: "unknown_method",
|
||||
}
|
||||
} else {
|
||||
InvokeDecision::Fallback {
|
||||
reason: "unknown_method",
|
||||
}
|
||||
}
|
||||
}
|
||||
210
archive/jit-cranelift/src/jit/rt.rs
Normal file
210
archive/jit-cranelift/src/jit/rt.rs
Normal file
@ -0,0 +1,210 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::backend::vm::VMValue;
|
||||
use crate::jit::abi::JitValue;
|
||||
|
||||
// Legacy TLS for hostcalls that still expect VMValue — keep for compatibility
|
||||
// Legacy VM args TLS — disabled in jit-direct-only (no-op/empty)
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
thread_local! { static LEGACY_VM_ARGS: RefCell<Vec<VMValue>> = RefCell::new(Vec::new()); }
|
||||
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
pub fn set_legacy_vm_args(args: &[VMValue]) {
|
||||
LEGACY_VM_ARGS.with(|cell| {
|
||||
let mut v = cell.borrow_mut();
|
||||
v.clear();
|
||||
v.extend_from_slice(args);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "jit-direct-only")]
|
||||
pub fn set_legacy_vm_args(_args: &[VMValue]) { /* no-op in jit-direct-only */
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "jit-direct-only"))]
|
||||
pub fn with_legacy_vm_args<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[VMValue]) -> R,
|
||||
{
|
||||
LEGACY_VM_ARGS.with(|cell| {
|
||||
let v = cell.borrow();
|
||||
f(&v)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "jit-direct-only")]
|
||||
pub fn with_legacy_vm_args<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[VMValue]) -> R,
|
||||
{
|
||||
f(&[])
|
||||
}
|
||||
|
||||
// New TLS for independent JIT ABI values
|
||||
thread_local! {
|
||||
static CURRENT_JIT_ARGS: RefCell<Vec<JitValue>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
pub fn set_current_jit_args(args: &[JitValue]) {
|
||||
CURRENT_JIT_ARGS.with(|cell| {
|
||||
let mut v = cell.borrow_mut();
|
||||
v.clear();
|
||||
v.extend_from_slice(args);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn with_jit_args<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&[JitValue]) -> R,
|
||||
{
|
||||
CURRENT_JIT_ARGS.with(|cell| {
|
||||
let v = cell.borrow();
|
||||
f(&v)
|
||||
})
|
||||
}
|
||||
|
||||
// === JIT runtime counters (minimal) ===
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
static B1_NORM_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||
static RET_BOOL_HINT_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||
static PHI_TOTAL_SLOTS: AtomicU64 = AtomicU64::new(0);
|
||||
static PHI_B1_SLOTS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
pub fn b1_norm_inc(delta: u64) {
|
||||
B1_NORM_COUNT.fetch_add(delta, Ordering::Relaxed);
|
||||
}
|
||||
pub fn b1_norm_get() -> u64 {
|
||||
B1_NORM_COUNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn ret_bool_hint_inc(delta: u64) {
|
||||
RET_BOOL_HINT_COUNT.fetch_add(delta, Ordering::Relaxed);
|
||||
}
|
||||
pub fn ret_bool_hint_get() -> u64 {
|
||||
RET_BOOL_HINT_COUNT.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn phi_total_inc(delta: u64) {
|
||||
PHI_TOTAL_SLOTS.fetch_add(delta, Ordering::Relaxed);
|
||||
}
|
||||
pub fn phi_total_get() -> u64 {
|
||||
PHI_TOTAL_SLOTS.load(Ordering::Relaxed)
|
||||
}
|
||||
pub fn phi_b1_inc(delta: u64) {
|
||||
PHI_B1_SLOTS.fetch_add(delta, Ordering::Relaxed);
|
||||
}
|
||||
pub fn phi_b1_get() -> u64 {
|
||||
PHI_B1_SLOTS.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// === 10.7c PoC: JIT Handle Registry (thread-local) ===
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod handles {
|
||||
use super::*;
|
||||
|
||||
thread_local! {
|
||||
static REG: RefCell<HandleRegistry> = RefCell::new(HandleRegistry::new());
|
||||
static CREATED: RefCell<Vec<u64>> = RefCell::new(Vec::new());
|
||||
static SCOPES: RefCell<Vec<usize>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
struct HandleRegistry {
|
||||
next: u64,
|
||||
map: HashMap<u64, Arc<dyn crate::box_trait::NyashBox>>, // BoxRef-compatible
|
||||
}
|
||||
|
||||
impl HandleRegistry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
next: 1,
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
fn to_handle(&mut self, obj: Arc<dyn crate::box_trait::NyashBox>) -> u64 {
|
||||
// Reuse existing handle if already present (pointer equality check)
|
||||
// For PoC simplicity, always assign new handle
|
||||
let h = self.next;
|
||||
self.next = self.next.saturating_add(1);
|
||||
self.map.insert(h, obj);
|
||||
if std::env::var("NYASH_JIT_HANDLE_DEBUG").ok().as_deref() == Some("1") {
|
||||
eprintln!("[JIT][handle] new h={}", h);
|
||||
}
|
||||
h
|
||||
}
|
||||
fn get(&self, h: u64) -> Option<Arc<dyn crate::box_trait::NyashBox>> {
|
||||
self.map.get(&h).cloned()
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn drop_handle(&mut self, h: u64) {
|
||||
self.map.remove(&h);
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
self.next = 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_handle(obj: Arc<dyn crate::box_trait::NyashBox>) -> u64 {
|
||||
let h = REG.with(|cell| cell.borrow_mut().to_handle(obj));
|
||||
CREATED.with(|c| c.borrow_mut().push(h));
|
||||
h
|
||||
}
|
||||
pub fn get(h: u64) -> Option<Arc<dyn crate::box_trait::NyashBox>> {
|
||||
REG.with(|cell| cell.borrow().get(h))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn clear() {
|
||||
REG.with(|cell| cell.borrow_mut().clear());
|
||||
}
|
||||
pub fn len() -> usize {
|
||||
REG.with(|cell| cell.borrow().map.len())
|
||||
}
|
||||
/// Tally handles by NyashBox type name (best-effort)
|
||||
pub fn type_tally() -> Vec<(String, usize)> {
|
||||
use std::collections::HashMap;
|
||||
REG.with(|cell| {
|
||||
let reg = cell.borrow();
|
||||
let mut map: HashMap<String, usize> = HashMap::new();
|
||||
for (_h, obj) in reg.map.iter() {
|
||||
let tn = obj.type_name().to_string();
|
||||
*map.entry(tn).or_insert(0) += 1;
|
||||
}
|
||||
let mut v: Vec<(String, usize)> = map.into_iter().collect();
|
||||
v.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
|
||||
v
|
||||
})
|
||||
}
|
||||
/// Snapshot current handle objects (Arc clones)
|
||||
pub fn snapshot_arcs() -> Vec<Arc<dyn crate::box_trait::NyashBox>> {
|
||||
REG.with(|cell| {
|
||||
let reg = cell.borrow();
|
||||
reg.map.values().cloned().collect()
|
||||
})
|
||||
}
|
||||
|
||||
// Scope management: track and clear handles created within a JIT call
|
||||
pub fn begin_scope() {
|
||||
CREATED.with(|c| {
|
||||
let cur_len = c.borrow().len();
|
||||
SCOPES.with(|s| s.borrow_mut().push(cur_len));
|
||||
});
|
||||
}
|
||||
pub fn end_scope_clear() {
|
||||
let start = SCOPES.with(|s| s.borrow_mut().pop()).unwrap_or(0);
|
||||
let to_drop: Vec<u64> = CREATED.with(|c| {
|
||||
let mut v = c.borrow_mut();
|
||||
let slice = v[start..].to_vec();
|
||||
v.truncate(start);
|
||||
slice
|
||||
});
|
||||
REG.with(|cell| {
|
||||
let mut reg = cell.borrow_mut();
|
||||
for h in to_drop {
|
||||
reg.map.remove(&h);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
172
archive/jit-cranelift/src/jit/semantics/clif.rs
Normal file
172
archive/jit-cranelift/src/jit/semantics/clif.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::Semantics;
|
||||
use crate::backend::vm::VMValue;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
|
||||
/// Minimal Semantics for Cranelift skeleton (Const/Add/Return)
|
||||
pub struct ClifSemanticsSkeleton {
|
||||
pub mem: HashMap<ValueId, VMValue>,
|
||||
pub ret_val: Option<VMValue>,
|
||||
}
|
||||
|
||||
impl ClifSemanticsSkeleton {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mem: HashMap::new(),
|
||||
ret_val: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Semantics for ClifSemanticsSkeleton {
|
||||
type Val = VMValue;
|
||||
type Ptr = ValueId;
|
||||
type BB = BasicBlockId;
|
||||
|
||||
// Constants
|
||||
fn const_i64(&mut self, v: i64) -> Self::Val {
|
||||
VMValue::Integer(v)
|
||||
}
|
||||
fn const_f64(&mut self, v: f64) -> Self::Val {
|
||||
VMValue::Float(v)
|
||||
}
|
||||
fn const_bool(&mut self, v: bool) -> Self::Val {
|
||||
VMValue::Bool(v)
|
||||
}
|
||||
fn const_null(&mut self) -> Self::Val {
|
||||
VMValue::Void
|
||||
}
|
||||
fn const_str(&mut self, s: &str) -> Self::Val {
|
||||
VMValue::String(s.to_string())
|
||||
}
|
||||
|
||||
// Unary/Binary/Compare (minimal)
|
||||
fn neg(&mut self, x: Self::Val) -> Self::Val {
|
||||
match x {
|
||||
VMValue::Integer(i) => VMValue::Integer(-i),
|
||||
VMValue::Float(f) => VMValue::Float(-f),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
fn not(&mut self, x: Self::Val) -> Self::Val {
|
||||
VMValue::Bool(matches!(
|
||||
x,
|
||||
VMValue::Bool(false) | VMValue::Integer(0) | VMValue::Void
|
||||
))
|
||||
}
|
||||
fn bit_not(&mut self, x: Self::Val) -> Self::Val {
|
||||
if let VMValue::Integer(i) = x {
|
||||
VMValue::Integer(!i)
|
||||
} else {
|
||||
x
|
||||
}
|
||||
}
|
||||
fn add(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
use VMValue as V;
|
||||
match (a, b) {
|
||||
(V::Integer(x), V::Integer(y)) => V::Integer(x + y),
|
||||
(V::Float(x), V::Float(y)) => V::Float(x + y),
|
||||
(V::Float(x), V::Integer(y)) => V::Float(x + y as f64),
|
||||
(V::Integer(x), V::Float(y)) => V::Float(x as f64 + y),
|
||||
(V::String(s), V::String(t)) => V::String(format!("{}{}", s, t)),
|
||||
(V::String(s), V::Integer(y)) => V::String(format!("{}{}", s, y)),
|
||||
(V::Integer(x), V::String(t)) => V::String(format!("{}{}", x, t)),
|
||||
(l, r) => {
|
||||
let s = format!("{}{}", l.to_string(), r.to_string());
|
||||
V::String(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn sub(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x - y),
|
||||
_ => VMValue::Void,
|
||||
}
|
||||
}
|
||||
fn mul(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Integer(x * y),
|
||||
_ => VMValue::Void,
|
||||
}
|
||||
}
|
||||
fn div(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x / y),
|
||||
_ => VMValue::Void,
|
||||
}
|
||||
}
|
||||
fn modulo(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) if y != 0 => VMValue::Integer(x % y),
|
||||
_ => VMValue::Void,
|
||||
}
|
||||
}
|
||||
fn cmp_eq(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
VMValue::Bool(a.to_string() == b.to_string())
|
||||
}
|
||||
fn cmp_ne(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
VMValue::Bool(a.to_string() != b.to_string())
|
||||
}
|
||||
fn cmp_lt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x < y),
|
||||
_ => VMValue::Bool(false),
|
||||
}
|
||||
}
|
||||
fn cmp_le(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x <= y),
|
||||
_ => VMValue::Bool(false),
|
||||
}
|
||||
}
|
||||
fn cmp_gt(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x > y),
|
||||
_ => VMValue::Bool(false),
|
||||
}
|
||||
}
|
||||
fn cmp_ge(&mut self, a: Self::Val, b: Self::Val) -> Self::Val {
|
||||
match (a, b) {
|
||||
(VMValue::Integer(x), VMValue::Integer(y)) => VMValue::Bool(x >= y),
|
||||
_ => VMValue::Bool(false),
|
||||
}
|
||||
}
|
||||
|
||||
// Memory & control (minimal)
|
||||
fn alloca_ptr(&mut self, vid: ValueId) -> Self::Ptr {
|
||||
vid
|
||||
}
|
||||
fn load(&mut self, ptr: &Self::Ptr) -> Self::Val {
|
||||
self.mem.get(ptr).cloned().unwrap_or(VMValue::Void)
|
||||
}
|
||||
fn store(&mut self, ptr: &Self::Ptr, v: Self::Val) {
|
||||
self.mem.insert(*ptr, v);
|
||||
}
|
||||
fn jump(&mut self, _target: BasicBlockId) {}
|
||||
fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) {}
|
||||
fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val {
|
||||
VMValue::Void
|
||||
}
|
||||
fn ret(&mut self, v: Option<Self::Val>) {
|
||||
self.ret_val = v;
|
||||
}
|
||||
|
||||
// Host/Box calls (unimplemented in skeleton)
|
||||
fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val {
|
||||
VMValue::Void
|
||||
}
|
||||
fn box_call_tagged(
|
||||
&mut self,
|
||||
_type_id: i64,
|
||||
_method_id: i64,
|
||||
_recv: Self::Val,
|
||||
_argv: &[Self::Val],
|
||||
_tags: &[i64],
|
||||
) -> Self::Val {
|
||||
VMValue::Void
|
||||
}
|
||||
fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val {
|
||||
VMValue::Void
|
||||
}
|
||||
}
|
||||
6
archive/jit-cranelift/src/jit/semantics/mod.rs
Normal file
6
archive/jit-cranelift/src/jit/semantics/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* JIT Semantics - Skeleton implementations used by Cranelift path
|
||||
*/
|
||||
|
||||
pub mod clif;
|
||||
pub use crate::semantics::Semantics; // re-export trait for local use
|
||||
53
archive/jit-cranelift/src/jit/shim_trace.rs
Normal file
53
archive/jit-cranelift/src/jit/shim_trace.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
static TRACE_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
static EVENTS: Lazy<Mutex<VecDeque<String>>> =
|
||||
Lazy::new(|| Mutex::new(VecDeque::with_capacity(256)));
|
||||
const MAX_EVENTS: usize = 256;
|
||||
|
||||
pub fn set_enabled(on: bool) {
|
||||
TRACE_ENABLED.store(on, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_enabled() -> bool {
|
||||
if TRACE_ENABLED.load(Ordering::Relaxed) {
|
||||
return true;
|
||||
}
|
||||
std::env::var("NYASH_JIT_SHIM_TRACE").ok().as_deref() == Some("1")
|
||||
}
|
||||
|
||||
pub fn push(event: String) {
|
||||
if !is_enabled() {
|
||||
return;
|
||||
}
|
||||
if let Ok(mut q) = EVENTS.lock() {
|
||||
if q.len() >= MAX_EVENTS {
|
||||
q.pop_front();
|
||||
}
|
||||
q.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot_joined() -> String {
|
||||
if let Ok(q) = EVENTS.lock() {
|
||||
let mut out = String::new();
|
||||
for (i, e) in q.iter().enumerate() {
|
||||
if i > 0 {
|
||||
out.push('\n');
|
||||
}
|
||||
out.push_str(e);
|
||||
}
|
||||
out
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear() {
|
||||
if let Ok(mut q) = EVENTS.lock() {
|
||||
q.clear();
|
||||
}
|
||||
}
|
||||
124
archive/jit-cranelift/src/semantics/clif_adapter.rs
Normal file
124
archive/jit-cranelift/src/semantics/clif_adapter.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use super::Semantics;
|
||||
use crate::jit::lower::builder::{BinOpKind, CmpKind, IRBuilder};
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
|
||||
/// Adapter that translates Semantics operations into IRBuilder calls (Cranelift path)
|
||||
pub struct ClifSemanticsAdapter<'a> {
|
||||
pub builder: &'a mut dyn IRBuilder,
|
||||
}
|
||||
|
||||
impl<'a> ClifSemanticsAdapter<'a> {
|
||||
pub fn new(builder: &'a mut dyn IRBuilder) -> Self {
|
||||
Self { builder }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Semantics for ClifSemanticsAdapter<'a> {
|
||||
type Val = ();
|
||||
type Ptr = ValueId;
|
||||
type BB = BasicBlockId;
|
||||
|
||||
fn const_i64(&mut self, v: i64) -> Self::Val {
|
||||
self.builder.emit_const_i64(v);
|
||||
}
|
||||
fn const_f64(&mut self, v: f64) -> Self::Val {
|
||||
self.builder.emit_const_f64(v);
|
||||
}
|
||||
fn const_bool(&mut self, v: bool) -> Self::Val {
|
||||
self.builder.emit_const_i64(if v { 1 } else { 0 });
|
||||
}
|
||||
fn const_null(&mut self) -> Self::Val {
|
||||
self.builder.emit_const_i64(0);
|
||||
}
|
||||
fn const_str(&mut self, _s: &str) -> Self::Val {
|
||||
self.builder.emit_const_i64(0);
|
||||
}
|
||||
|
||||
fn neg(&mut self, _x: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Sub);
|
||||
}
|
||||
fn not(&mut self, _x: Self::Val) -> Self::Val { /* handled via compare/select in LowerCore */
|
||||
}
|
||||
fn bit_not(&mut self, _x: Self::Val) -> Self::Val { /* not used here */
|
||||
}
|
||||
fn add(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Add);
|
||||
}
|
||||
fn sub(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Sub);
|
||||
}
|
||||
fn mul(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Mul);
|
||||
}
|
||||
fn div(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Div);
|
||||
}
|
||||
fn modulo(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_binop(BinOpKind::Mod);
|
||||
}
|
||||
fn cmp_eq(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Eq);
|
||||
}
|
||||
fn cmp_ne(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Ne);
|
||||
}
|
||||
fn cmp_lt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Lt);
|
||||
}
|
||||
fn cmp_le(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Le);
|
||||
}
|
||||
fn cmp_gt(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Gt);
|
||||
}
|
||||
fn cmp_ge(&mut self, _a: Self::Val, _b: Self::Val) -> Self::Val {
|
||||
self.builder.emit_compare(CmpKind::Ge);
|
||||
}
|
||||
|
||||
fn alloca_ptr(&mut self, _vid: ValueId) -> Self::Ptr {
|
||||
_vid
|
||||
}
|
||||
fn load(&mut self, _ptr: &Self::Ptr) -> Self::Val {
|
||||
self.builder.load_local_i64(_ptr.as_u32() as usize);
|
||||
}
|
||||
fn store(&mut self, _ptr: &Self::Ptr, _v: Self::Val) {
|
||||
self.builder.store_local_i64(_ptr.as_u32() as usize);
|
||||
}
|
||||
fn jump(&mut self, _target: BasicBlockId) { /* handled by LowerCore */
|
||||
}
|
||||
fn branch(&mut self, _cond: Self::Val, _then_bb: BasicBlockId, _else_bb: BasicBlockId) {
|
||||
/* handled by LowerCore */
|
||||
}
|
||||
fn phi_select(&mut self, _incoming: &[(BasicBlockId, Self::Val)]) -> Self::Val {
|
||||
()
|
||||
}
|
||||
fn ret(&mut self, _v: Option<Self::Val>) {
|
||||
self.builder.emit_return();
|
||||
}
|
||||
|
||||
fn new_box(&mut self, _type_id: i64, _args: &[Self::Val]) -> Self::Val {
|
||||
()
|
||||
}
|
||||
fn box_call_tagged(
|
||||
&mut self,
|
||||
_type_id: i64,
|
||||
_method_id: i64,
|
||||
_recv: Self::Val,
|
||||
_argv: &[Self::Val],
|
||||
_tags: &[i64],
|
||||
) -> Self::Val {
|
||||
()
|
||||
}
|
||||
fn extern_call(&mut self, _iface: &str, _method: &str, _args: &[Self::Val]) -> Self::Val {
|
||||
()
|
||||
}
|
||||
|
||||
fn barrier_read(&mut self, v: Self::Val) -> Self::Val {
|
||||
v
|
||||
}
|
||||
fn barrier_write(&mut self, _ptr: &Self::Ptr, v: Self::Val) -> Self::Val {
|
||||
v
|
||||
}
|
||||
fn safepoint(&mut self) { /* Lowered via explicit hostcall in LowerCore path */
|
||||
}
|
||||
}
|
||||
20
archive/jit-cranelift/tools/jit_compare_smoke.sh
Normal file
20
archive/jit-cranelift/tools/jit_compare_smoke.sh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
echo "[build] nyash (cranelift-jit)"
|
||||
cargo build --release --features cranelift-jit
|
||||
|
||||
run_case() {
|
||||
local app="$1"
|
||||
echo "[run] jit-direct: $app"
|
||||
NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct "$app"
|
||||
}
|
||||
|
||||
run_case apps/tests/mir-branch-ret/main.nyash
|
||||
run_case apps/tests/mir-compare-multi/main.nyash
|
||||
|
||||
echo "[ok] jit compare smokes completed"
|
||||
|
||||
20
archive/jit-cranelift/tools/smokes/jit_smoke.sh
Normal file
20
archive/jit-cranelift/tools/smokes/jit_smoke.sh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Archived: JIT smoke (not maintained in current phase). Kept for reference.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/../../" && pwd)
|
||||
|
||||
BIN="$ROOT_DIR/target/release/nyash"
|
||||
if [ ! -x "$BIN" ]; then
|
||||
echo "Building nyash (release, JIT)..." >&2
|
||||
cargo build --release --features cranelift-jit >/dev/null
|
||||
fi
|
||||
|
||||
echo "[JIT Smoke] Core VM/JIT (plugins disabled)" >&2
|
||||
NYASH_DISABLE_PLUGINS=1 NYASH_CLI_VERBOSE=1 "$ROOT_DIR/tools/smokes/archive/smoke_vm_jit.sh" >/tmp/nyash-jit-core.out
|
||||
grep -q '^✅ smoke done' /tmp/nyash-jit-core.out || { echo "FAIL: core VM/JIT smoke" >&2; cat /tmp/nyash-jit-core.out; exit 1; }
|
||||
echo "PASS: core VM/JIT smoke" >&2
|
||||
|
||||
echo "All PASS (archived JIT smoke)" >&2
|
||||
|
||||
Reference in New Issue
Block a user