From 3c3dc86be093fc86ace5970f4f97ebd300e43561 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Sun, 24 Aug 2025 00:05:12 +0900 Subject: [PATCH] feat: MIR builder TypeOp lowering for is/as methods and isType/asType functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add early TypeOp lowering in build_expression for method-style is()/as() - Add early TypeOp lowering in build_expression for function-style isType()/asType() - Add special handling in build_print_statement for print(isType/asType(...)) - Fix MIR optimizer borrow checker issues and remove obsolete BoxFieldLoad - Extract string literal helper supports both direct literals and StringBox wrappers - Note: isType call generation still has issues (undefined SSA value in print) 🤖 Generated with Claude Code Co-Authored-By: Claude --- docs/development/current/CURRENT_TASK.md | 45 +++-- nekocode-temp | 2 +- src/interpreter/functions.rs | 59 +++++++ src/interpreter/method_dispatch.rs.legacy | 16 ++ src/mir/builder.rs | 202 ++++++++++++++++++++-- src/mir/mod.rs | 8 +- src/mir/optimizer.rs | 118 ++++++++++++- tools/vm_stats_diff.sh | 27 +++ vm_stats_weak_default.json | 75 ++++++++ vm_stats_weak_unified.json | 65 +++++++ 10 files changed, 584 insertions(+), 33 deletions(-) create mode 100644 tools/vm_stats_diff.sh create mode 100644 vm_stats_weak_default.json create mode 100644 vm_stats_weak_unified.json diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 7adecb20..2d18f81e 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -36,26 +36,26 @@ ## 🚧 次にやること(再開方針) -1) 命令セットダイエットのPoC実装(短期) - - 現状: VMに `TypeOp/WeakRef/Barrier` 実行経路(等価)とPrinter対応。Builderに補助APIを追加済(未置換)。 - - 次: Builder内の該当箇所を補助APIに置換(flag onで新命令を吐く/offで従来どおり) +1) MIR26 前進(短期) + - プリンタ拡張: `TypeOp/WeakRef/Barrier` を `--mir-verbose` に明示表示 + - スナップショット整備: 代表ケースで flag ON/OFF のMIR差分固定化 + - vm-stats差分: `weak_field_poc.nyash` 等で JSON 取得・比較(キー: TypeOp/WeakRef/Barrier) - 旗: `mir_typeop_poc`(TypeCheck/Cast→TypeOp)、`mir_refbarrier_unify_poc`(Weak*/Barrier→統合) - - 成果物: スナップショット(flag on/off)+ vm-statsのキー確認(TypeOp/WeakRef/Barrier) -2) VM×プラグインのE2E拡張(短期) - - HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証(代表は追加済) - - Socket: 反復タイムアウトの追加ケース(代表は追加済) - - 成果物: 必要に応じてE2E追補と `VM_README.md` のTips更新 +2) Builder適用拡大(短期〜中期) + - 言語 `is/as` 導線(最小でも擬似ノード)→ `emit_type_check/emit_cast` へ配線 + - 弱参照: 既存の `RefGet/RefSet` パスは弱フィールドで `WeakLoad/WeakNew`+Barrier(flag ONで統合命令) -3) ResultBox単一路線への統合(中期) -- 新`NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行 -- 成果物: 実装整理・移行メモ・影響調査 +3) VM/Verifierの補強(中期) + - `TypeOp(Cast)` の数値キャスト(Int/Float)安全化、誤用時TypeError整備 + - Verifierに26命令整合(Barrier位置、WeakRef整合、支配関係)チェックを追加 -4) Array系の本実装(必要時・中期) - - VMの `ArrayGet/ArraySet` 実装済み。BoxCall fast-pathの整合性と回帰テストを充実 +4) VM×プラグインE2Eの維持(短期) + - HTTP/Socketの回帰確認(Void防御・遅延サーバ軽量化は済) + - 必要に応じて `VM_README.md` にTips追記 5) BoxCall高速化(性能段階) -- vm-statsでホットなBoxCallの高速化(命令セット統合より効果大の可能性) + - `--vm-stats` ホットパス特定後、Fast-path/キャッシュ適用 ## ▶ 実行コマンド例 @@ -73,6 +73,21 @@ cargo test -q --features plugins e2e_interpreter_plugin_filebox_close_void cargo test -q --features plugins e2e_vm_plugin_filebox_close_void ``` +MIR26 PoC(弱参照・Barrier統合): +```bash +# 弱フィールドPoC(flag OFF: WeakNew/WeakLoad/BarrierRead/Write) +NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/release/nyash --backend vm --vm-stats --vm-stats-json local_tests/weak_field_poc.nyash > vm_stats_weak_default.json + +# flag ON: WeakRef/Barrier 統合 +cargo build --release --features mir_refbarrier_unify_poc -q +NYASH_VM_STATS=1 NYASH_VM_STATS_JSON=1 ./target/release/nyash --backend vm --vm-stats --vm-stats-json local_tests/weak_field_poc.nyash > vm_stats_weak_unified.json +``` + +MIRダンプ(プリンタ拡張後の確認): +```bash +./target/release/nyash --dump-mir --mir-verbose local_tests/weak_field_poc.nyash | sed -n '1,200p' +``` + MIRダンプ/検証: ```bash nyash --dump-mir --mir-verbose examples/plugin_box_sample.nyash @@ -84,7 +99,7 @@ nyash --verify examples/plugin_box_sample.nyash - メタ降格: Debug / Nop / Safepoint(ビルドモードで制御) --- -最終更新: 2025年8月23日(VM強化・E2E拡張・me参照安定化・TypeOp/WeakRef/Barrier PoC完了/次段はBuilder置換とスナップショット) +最終更新: 2025年8月23日(VM強化・E2E拡張・me参照安定化・TypeOp/WeakRef/Barrier PoC完了/次段はプリンタ拡張・スナップショット・is/as導線) ## 🔁 再起動後の再開手順(ショート) ```bash diff --git a/nekocode-temp b/nekocode-temp index 509c8b7d..9e0879ba 160000 --- a/nekocode-temp +++ b/nekocode-temp @@ -1 +1 @@ -Subproject commit 509c8b7de8be76e0697bf034495a2d7ec138c174 +Subproject commit 9e0879ba14e906b7476af7379ef334f6d4d1dcdf diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index 7add8998..80d18e9a 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -12,6 +12,26 @@ impl NyashInterpreter { /// 関数呼び出しを実行 - 🌍 革命的実装:GlobalBoxのメソッド呼び出し pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode]) -> Result, RuntimeError> { + // Fallback: built-in type ops as global functions: isType(value, "Type"), asType(value, "Type") + if (name == "isType" || name == "asType") && arguments.len() == 2 { + // Evaluate args + let val = self.execute_expression(&arguments[0])?; + let ty_box = self.execute_expression(&arguments[1])?; + // Get type name string + let type_name = if let Some(s) = ty_box.as_any().downcast_ref::() { + s.value.clone() + } else { + return Err(RuntimeError::InvalidOperation { message: "Type name must be a string".to_string() }); + }; + + if name == "isType" { + let matched = Self::matches_type_name(&val, &type_name); + return Ok(Box::new(crate::box_trait::BoolBox::new(matched))); + } else { + // asType: minimal safe cast (int<->float), otherwise identity + return Self::cast_to_type(val, &type_name); + } + } // コンストラクタ内での親コンストラクタ呼び出しチェック if let Some(context) = self.current_constructor_context.clone() { if let Some(parent_class) = context.parent_class { @@ -94,4 +114,43 @@ impl NyashInterpreter { eprintln!("Warning: Failed to register global function: {}", err); }); } + + /// Helper: match a NyashBox value against a simple type name + fn matches_type_name(val: &Box, type_name: &str) -> bool { + let tn = val.type_name(); + match type_name { + "Integer" | "Int" | "I64" => tn == "IntegerBox", + "Float" | "F64" => tn == "FloatBox", + "Bool" | "Boolean" => tn == "BoolBox", + "String" => tn == "StringBox", + "Void" | "Unit" => tn == "VoidBox", + other => tn == other || tn == format!("{}Box", other), + } + } + + /// Helper: cast box to a target type name (minimal support) + fn cast_to_type(val: Box, type_name: &str) -> Result, RuntimeError> { + match type_name { + "Integer" | "Int" | "I64" => { + // Float -> Integer (truncate), Integer -> Integer, else error + if let Some(i) = val.as_any().downcast_ref::() { + Ok(Box::new(crate::box_trait::IntegerBox::new(i.value))) + } else if let Some(f) = val.as_any().downcast_ref::() { + Ok(Box::new(crate::box_trait::IntegerBox::new(f.value as i64))) + } else { + Ok(val) // identity fallback for now + } + } + "Float" | "F64" => { + if let Some(f) = val.as_any().downcast_ref::() { + Ok(Box::new(crate::boxes::FloatBox::new(f.value))) + } else if let Some(i) = val.as_any().downcast_ref::() { + Ok(Box::new(crate::boxes::FloatBox::new(i.value as f64))) + } else { + Ok(val) + } + } + _ => Ok(val), + } + } } diff --git a/src/interpreter/method_dispatch.rs.legacy b/src/interpreter/method_dispatch.rs.legacy index 14495b86..6240c4d9 100644 --- a/src/interpreter/method_dispatch.rs.legacy +++ b/src/interpreter/method_dispatch.rs.legacy @@ -46,6 +46,22 @@ impl NyashInterpreter { // オブジェクトを評価(通常のメソッド呼び出し) let obj_value = self.execute_expression(object)?; + + // Fallback: built-in type ops as instance methods: value.is("Type"), value.as("Type") + if (method == "is" || method == "as") && arguments.len() == 1 { + let ty_box = self.execute_expression(&arguments[0])?; + let type_name = if let Some(s) = ty_box.as_any().downcast_ref::() { + s.value.clone() + } else { + return Err(RuntimeError::InvalidOperation { message: "Type name must be a string".to_string() }); + }; + if method == "is" { + let matched = super::functions::NyashInterpreter::matches_type_name(&obj_value, &type_name); + return Ok(Box::new(crate::box_trait::BoolBox::new(matched))); + } else { + return super::functions::NyashInterpreter::cast_to_type(obj_value, &type_name); + } + } eprintln!("🔍 DEBUG: execute_method_call - object evaluated to type_name='{}', box_id={}", obj_value.type_name(), obj_value.box_id()); diff --git a/src/mir/builder.rs b/src/mir/builder.rs index bf5fa237..8c1ac61f 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -43,6 +43,12 @@ pub struct MirBuilder { /// Names of user-defined boxes declared in the current module pub(super) user_defined_boxes: HashSet, + + /// Weak field registry: BoxName -> {weak field names} + pub(super) weak_fields_by_box: HashMap>, + + /// Remember class of object fields after assignments: (base_id, field) -> class_name + pub(super) field_origin_class: HashMap<(ValueId, String), String>, } impl MirBuilder { @@ -58,6 +64,8 @@ impl MirBuilder { pending_phis: Vec::new(), value_origin_newbox: HashMap::new(), user_defined_boxes: HashSet::new(), + weak_fields_by_box: HashMap::new(), + field_origin_class: HashMap::new(), } } @@ -203,6 +211,8 @@ impl MirBuilder { let me_id = self.value_gen.next(); f.params.push(me_id); self.variable_map.insert("me".to_string(), me_id); + // Record origin: 'me' belongs to this box type (enables weak field wiring) + self.value_origin_newbox.insert(me_id, box_name.clone()); // user parameters continue as %1..N for p in ¶ms { let pid = self.value_gen.next(); @@ -318,6 +328,17 @@ impl MirBuilder { }, ASTNode::MethodCall { object, method, arguments, .. } => { + // Early TypeOp lowering for method-style is()/as() + if (method == "is" || method == "as") && arguments.len() == 1 { + if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { + let obj_val = self.build_expression(*object.clone())?; + let ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?; + return Ok(dst); + } + } self.build_method_call(*object.clone(), method.clone(), arguments.clone()) }, @@ -338,6 +359,17 @@ impl MirBuilder { }, ASTNode::FunctionCall { name, arguments, .. } => { + // Early TypeOp lowering for function-style isType()/asType() + if (name == "isType" || name == "asType") && arguments.len() == 2 { + if let Some(type_name) = Self::extract_string_literal(&arguments[1]) { + let val = self.build_expression(arguments[0].clone())?; + let ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + return Ok(dst); + } + } self.build_function_call(name.clone(), arguments.clone()) }, @@ -389,14 +421,14 @@ impl MirBuilder { self.build_local_statement(variables.clone(), initial_values.clone()) }, - ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, .. } => { + ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => { if is_static && name == "Main" { self.build_static_main_box(methods.clone()) } else { // Support user-defined boxes - handle as statement, return void // Track as user-defined (eligible for method lowering) self.user_defined_boxes.insert(name.clone()); - self.build_box_declaration(name.clone(), methods.clone(), fields.clone())?; + self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?; // Phase 2: Lower constructors (birth/N) into MIR functions // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1") @@ -535,6 +567,17 @@ impl MirBuilder { /// Build function call fn build_function_call(&mut self, name: String, args: Vec) -> Result { + // Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type") + if (name == "isType" || name == "asType") && args.len() == 2 { + if let Some(type_name) = Self::extract_string_literal(&args[1]) { + let val = self.build_expression(args[0].clone())?; + let ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + return Ok(dst); + } + } // Build argument values let mut arg_values = Vec::new(); for arg in args { @@ -563,6 +606,24 @@ impl MirBuilder { /// Build print statement - converts to console output fn build_print_statement(&mut self, expression: ASTNode) -> Result { + // Early lowering for print(isType(...)) / print(asType(...)) to avoid undefined SSA when optimizations run + if let ASTNode::FunctionCall { name, arguments, .. } = &expression { + if (name == "isType" || name == "asType") && arguments.len() == 2 { + if let Some(type_name) = Self::extract_string_literal(&arguments[1]) { + let val = self.build_expression(arguments[0].clone())?; + let ty = Self::parse_type_name_to_mir(&type_name); + let dst = self.value_gen.next(); + let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; + self.emit_instruction(MirInstruction::Print { + value: dst, + effects: EffectMask::PURE.add(Effect::Io), + })?; + return Ok(dst); + } + } + } + let value = self.build_expression(expression)?; // For now, use a special Print instruction (minimal scope) @@ -885,18 +946,51 @@ impl MirBuilder { /// Build field access: object.field fn build_field_access(&mut self, object: ASTNode, field: String) -> Result { + // Clone the object before building expression if we need to check it later + let object_clone = object.clone(); + // First, build the object expression to get its ValueId let object_value = self.build_expression(object)?; - + // Get the field from the object using RefGet - let result_id = self.value_gen.next(); + let field_val = self.value_gen.next(); self.emit_instruction(MirInstruction::RefGet { - dst: result_id, + dst: field_val, reference: object_value, - field, + field: field.clone(), })?; - - Ok(result_id) + + // If we recorded origin class for this field on this base object, propagate it to this value id + if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() { + self.value_origin_newbox.insert(field_val, class_name); + } + + // If we can infer the box type and the field is weak, emit WeakLoad (+ optional barrier) + let mut inferred_class: Option = self.value_origin_newbox.get(&object_value).cloned(); + // Fallback: if the object is a nested field access like (X.Y).Z, consult recorded field origins for X.Y + if inferred_class.is_none() { + if let ASTNode::FieldAccess { object: inner_obj, field: ref parent_field, .. } = object_clone { + // Build inner base to get a stable id and consult mapping + if let Ok(base_id) = self.build_expression(*inner_obj.clone()) { + if let Some(cls) = self.field_origin_class.get(&(base_id, parent_field.clone())) { + inferred_class = Some(cls.clone()); + } + } + } + } + if let Some(class_name) = inferred_class { + if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { + if weak_set.contains(&field) { + // Barrier (read) PoC + let _ = self.emit_barrier_read(field_val); + // WeakLoad + let loaded = self.emit_weak_load(field_val)?; + return Ok(loaded); + } + } + } + + Ok(field_val) } /// Build new expression: new ClassName(arguments) @@ -941,15 +1035,38 @@ impl MirBuilder { fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result { // Build the object and value expressions let object_value = self.build_expression(object)?; - let value_result = self.build_expression(value)?; - + let mut value_result = self.build_expression(value)?; + + // If we can infer the box type and the field is weak, create WeakRef before store + if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { + if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { + if weak_set.contains(&field) { + value_result = self.emit_weak_new(value_result)?; + } + } + } + // Set the field using RefSet self.emit_instruction(MirInstruction::RefSet { reference: object_value, - field, + field: field.clone(), value: value_result, })?; - + + // Emit a write barrier for weak fields (PoC) + if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { + if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { + if weak_set.contains(&field) { + let _ = self.emit_barrier_write(value_result); + } + } + } + + // Record origin class for this field (if the value originates from NewBox of a known class) + if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() { + self.field_origin_class.insert((object_value, field.clone()), class_name); + } + // Return the assigned value Ok(value_result) } @@ -1059,6 +1176,19 @@ impl MirBuilder { /// Build method call: object.method(arguments) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec) -> Result { + // Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type") + if (method == "is" || method == "as") && arguments.len() == 1 { + if let ASTNode::Literal { value: crate::ast::LiteralValue::String(type_name), .. } = &arguments[0] { + // Build the object expression + let object_value = self.build_expression(object.clone())?; + // Map string to MIR type + let mir_ty = Self::parse_type_name_to_mir(type_name); + let dst = self.value_gen.next(); + let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + return Ok(dst); + } + } // ExternCall判定はobjectの変数解決より先に行う(未定義変数で落とさない) if let ASTNode::Variable { name: object_name, .. } = object.clone() { // Build argument expressions first (externはobject自体を使わない) @@ -1098,6 +1228,17 @@ impl MirBuilder { // Build the object expression let object_value = self.build_expression(object.clone())?; + // Secondary interception for is/as in case early path did not trigger + if (method == "is" || method == "as") && arguments.len() == 1 { + if let ASTNode::Literal { value: crate::ast::LiteralValue::String(type_name), .. } = &arguments[0] { + let mir_ty = Self::parse_type_name_to_mir(type_name); + let dst = self.value_gen.next(); + let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; + self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?; + return Ok(dst); + } + } + // Build argument expressions let mut arg_values = Vec::new(); for arg in &arguments { @@ -1161,6 +1302,35 @@ impl MirBuilder { })?; Ok(result_id) } + + /// Map a user-facing type name to MIR type + fn parse_type_name_to_mir(name: &str) -> super::MirType { + match name { + "Integer" | "Int" | "I64" => super::MirType::Integer, + "Float" | "F64" => super::MirType::Float, + "Bool" | "Boolean" => super::MirType::Bool, + "String" => super::MirType::String, + "Void" | "Unit" => super::MirType::Void, + other => super::MirType::Box(other.to_string()), + } + } + + /// Extract string literal from AST node if possible + /// Supports: Literal("Type") and new StringBox("Type") + fn extract_string_literal(node: &ASTNode) -> Option { + match node { + ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } => Some(s.clone()), + ASTNode::New { class, arguments, .. } if class == "StringBox" => { + if arguments.len() == 1 { + if let ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } = &arguments[0] { + return Some(s.clone()); + } + } + None + } + _ => None, + } + } /// Build from expression: from Parent.method(arguments) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec) -> Result { @@ -1193,7 +1363,7 @@ impl MirBuilder { } /// Build box declaration: box Name { fields... methods... } - fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap, fields: Vec) -> Result<(), String> { + fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap, fields: Vec, weak_fields: Vec) -> Result<(), String> { // For Phase 8.4, we'll emit metadata instructions to register the box type // In a full implementation, this would register type information for later use @@ -1212,6 +1382,12 @@ impl MirBuilder { value: ConstValue::String(format!("__field_{}_{}", name, field)), })?; } + + // Record weak fields for this box + if !weak_fields.is_empty() { + let set: HashSet = weak_fields.into_iter().collect(); + self.weak_fields_by_box.insert(name.clone(), set); + } // Process methods - now methods is a HashMap for (method_name, method_ast) in methods { diff --git a/src/mir/mod.rs b/src/mir/mod.rs index e01a51ae..30d8037e 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -16,6 +16,7 @@ pub mod ownership_verifier_simple; // Simple ownership forest verification for c pub mod printer; pub mod value_id; pub mod effect; +pub mod optimizer; // Re-export main types for easy access pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp}; @@ -28,6 +29,7 @@ pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, Ownership pub use printer::MirPrinter; pub use value_id::{ValueId, LocalId, ValueIdGenerator}; pub use effect::{EffectMask, Effect}; +pub use optimizer::MirOptimizer; /// MIR compilation result #[derive(Debug, Clone)] @@ -54,7 +56,11 @@ impl MirCompiler { /// Compile AST to MIR module with verification pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result { // Convert AST to MIR using builder - let module = self.builder.build_module(ast)?; + let mut module = self.builder.build_module(ast)?; + + // Optimize (safety net lowering for is/as → TypeOp 等) + let mut optimizer = MirOptimizer::new(); + let _ = optimizer.optimize_module(&mut module); // Verify the generated MIR let verification_result = self.verifier.verify_module(&module); diff --git a/src/mir/optimizer.rs b/src/mir/optimizer.rs index 4d56ac4d..d715fb95 100644 --- a/src/mir/optimizer.rs +++ b/src/mir/optimizer.rs @@ -8,7 +8,7 @@ * - Dead code elimination */ -use super::{MirModule, MirFunction, MirInstruction, ValueId, EffectMask, Effect}; +use super::{MirModule, MirFunction, MirInstruction, ValueId, MirType, TypeOpKind}; use std::collections::{HashMap, HashSet}; /// MIR optimization passes @@ -50,6 +50,9 @@ impl MirOptimizer { // Pass 4: Intrinsic function optimization stats.merge(self.optimize_intrinsic_calls(module)); + + // Pass 4.5: Lower well-known intrinsics (is/as → TypeOp) as a safety net + stats.merge(self.lower_type_ops(module)); // Pass 5: BoxField dependency optimization stats.merge(self.optimize_boxfield_operations(module)); @@ -196,7 +199,8 @@ impl MirOptimizer { MirInstruction::Const { value, .. } => format!("const_{:?}", value), MirInstruction::BinOp { op, lhs, rhs, .. } => format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), MirInstruction::Compare { op, lhs, rhs, .. } => format!("cmp_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), - MirInstruction::BoxFieldLoad { box_val, field, .. } => format!("boxload_{}_{}", box_val.as_u32(), field), + // BoxFieldLoad removed from instruction set + // MirInstruction::BoxFieldLoad { box_val, field, .. } => format!("boxload_{}_{}", box_val.as_u32(), field), MirInstruction::Call { func, args, .. } => { let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::>().join(","); format!("call_{}_{}", func.as_u32(), args_str) @@ -253,6 +257,102 @@ impl MirOptimizer { // 3. Identity elimination (e.g., x + 0 → x) 0 } + + /// Safety-net lowering: convert known is/as patterns into TypeOp when possible + fn lower_type_ops(&mut self, module: &mut MirModule) -> OptimizationStats { + let mut stats = OptimizationStats::new(); + for (_name, function) in &mut module.functions { + stats.intrinsic_optimizations += self.lower_type_ops_in_function(function); + } + stats + } + + fn lower_type_ops_in_function(&mut self, function: &mut MirFunction) -> usize { + // Build a simple definition map: ValueId -> (block_id, index) + let mut def_map: std::collections::HashMap = std::collections::HashMap::new(); + for (bb_id, block) in &function.blocks { + for (i, inst) in block.instructions.iter().enumerate() { + if let Some(dst) = inst.dst_value() { + def_map.insert(dst, (*bb_id, i)); + } + } + if let Some(term) = &block.terminator { + if let Some(dst) = term.dst_value() { + def_map.insert(dst, (*bb_id, usize::MAX)); + } + } + } + + let mut lowered = 0; + + // Collect replacements first to avoid borrow checker issues + let mut replacements: Vec<(super::basic_block::BasicBlockId, usize, MirInstruction)> = Vec::new(); + + // First pass: identify replacements + for (bb_id, block) in &function.blocks { + for i in 0..block.instructions.len() { + let replace = match &block.instructions[i] { + MirInstruction::BoxCall { dst, box_val, method, args, .. } => { + let is_is = method == "is" || method == "isType"; + let is_as = method == "as" || method == "asType"; + if (is_is || is_as) && args.len() == 1 { + // Try to resolve type name from arg + let arg_id = args[0]; + if let Some((def_bb, def_idx)) = def_map.get(&arg_id).copied() { + // Look for pattern: NewBox StringBox(Const String) + if let Some(def_block) = function.blocks.get(&def_bb) { + if def_idx < def_block.instructions.len() { + if let MirInstruction::NewBox { box_type, args: sb_args, .. } = &def_block.instructions[def_idx] { + if box_type == "StringBox" && sb_args.len() == 1 { + let str_id = sb_args[0]; + if let Some((sbb, sidx)) = def_map.get(&str_id).copied() { + if let Some(sblock) = function.blocks.get(&sbb) { + if sidx < sblock.instructions.len() { + if let MirInstruction::Const { value, .. } = &sblock.instructions[sidx] { + if let super::instruction::ConstValue::String(s) = value { + // Build TypeOp to replace + let ty = map_type_name(s); + let op = if is_is { TypeOpKind::Check } else { TypeOpKind::Cast }; + let new_inst = MirInstruction::TypeOp { + dst: dst.unwrap_or(*box_val), + op, + value: *box_val, + ty, + }; + Some(new_inst) + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } else { None } + } + _ => None, + }; + + if let Some(new_inst) = replace { + replacements.push((*bb_id, i, new_inst)); + } + } + } + + // Second pass: apply replacements + for (bb_id, idx, new_inst) in replacements { + if let Some(block) = function.blocks.get_mut(&bb_id) { + if idx < block.instructions.len() { + block.instructions[idx] = new_inst; + lowered += 1; + } + } + } + + lowered + } /// Optimize BoxField operations fn optimize_boxfield_operations(&mut self, module: &mut MirModule) -> OptimizationStats { @@ -280,6 +380,18 @@ impl MirOptimizer { } } +/// Map string type name to MIR type (optimizer-level helper) +fn map_type_name(name: &str) -> MirType { + match name { + "Integer" | "Int" | "I64" => MirType::Integer, + "Float" | "F64" => MirType::Float, + "Bool" | "Boolean" => MirType::Bool, + "String" => MirType::String, + "Void" | "Unit" => MirType::Void, + other => MirType::Box(other.to_string()), + } +} + impl Default for MirOptimizer { fn default() -> Self { Self::new() @@ -377,4 +489,4 @@ mod tests { assert!(key.contains("const")); assert!(key.contains("42")); } -} \ No newline at end of file +} diff --git a/tools/vm_stats_diff.sh b/tools/vm_stats_diff.sh new file mode 100644 index 00000000..6daa5193 --- /dev/null +++ b/tools/vm_stats_diff.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ $# -ne 2 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +A="$1" +B="$2" + +if [ ! -f "$A" ] || [ ! -f "$B" ]; then + echo "Input files not found" >&2 + exit 1 +fi + +# Extract counts objects and join keys +KEYS=$(jq -r '.counts | keys[]' "$A" "$B" | sort -u) + +printf "%-14s %8s %8s %8s\n" "OP" "A" "B" "+/-" +for k in $KEYS; do + va=$(jq -r --arg k "$k" '.counts[$k] // 0' "$A") + vb=$(jq -r --arg k "$k" '.counts[$k] // 0' "$B") + d=$(( vb - va )) + printf "%-14s %8d %8d %8d\n" "$k" "$va" "$vb" "$d" +done | sort -k1,1 + diff --git a/vm_stats_weak_default.json b/vm_stats_weak_default.json new file mode 100644 index 00000000..0c651eae --- /dev/null +++ b/vm_stats_weak_default.json @@ -0,0 +1,75 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/weak_field_poc.nyash 🚀 +0 +{ + "counts": { + "BarrierRead": 1, + "BarrierWrite": 1, + "BoxCall": 2, + "Const": 9, + "NewBox": 2, + "Print": 1, + "RefGet": 4, + "RefSet": 3, + "Return": 3, + "Safepoint": 1, + "WeakLoad": 1, + "WeakNew": 1 + }, + "elapsed_ms": 0.194731, + "timestamp_ms": 1755956955634, + "top20": [ + { + "count": 9, + "op": "Const" + }, + { + "count": 4, + "op": "RefGet" + }, + { + "count": 3, + "op": "RefSet" + }, + { + "count": 3, + "op": "Return" + }, + { + "count": 2, + "op": "BoxCall" + }, + { + "count": 2, + "op": "NewBox" + }, + { + "count": 1, + "op": "BarrierRead" + }, + { + "count": 1, + "op": "BarrierWrite" + }, + { + "count": 1, + "op": "Print" + }, + { + "count": 1, + "op": "Safepoint" + }, + { + "count": 1, + "op": "WeakLoad" + }, + { + "count": 1, + "op": "WeakNew" + } + ], + "total": 29 +} +✅ VM execution completed successfully! +Result: IntegerBox { value: 0, base: BoxBase { id: 4, parent_type_id: None } } diff --git a/vm_stats_weak_unified.json b/vm_stats_weak_unified.json new file mode 100644 index 00000000..c768b641 --- /dev/null +++ b/vm_stats_weak_unified.json @@ -0,0 +1,65 @@ +🔌 v2 plugin system initialized from nyash.toml +✅ v2 plugin system fully configured +🚀 Nyash VM Backend - Executing file: local_tests/weak_field_poc.nyash 🚀 +0 +{ + "counts": { + "Barrier": 2, + "BoxCall": 2, + "Const": 9, + "NewBox": 2, + "Print": 1, + "RefGet": 4, + "RefSet": 3, + "Return": 3, + "Safepoint": 1, + "WeakRef": 2 + }, + "elapsed_ms": 0.16804799999999998, + "timestamp_ms": 1755957108401, + "top20": [ + { + "count": 9, + "op": "Const" + }, + { + "count": 4, + "op": "RefGet" + }, + { + "count": 3, + "op": "RefSet" + }, + { + "count": 3, + "op": "Return" + }, + { + "count": 2, + "op": "Barrier" + }, + { + "count": 2, + "op": "BoxCall" + }, + { + "count": 2, + "op": "NewBox" + }, + { + "count": 2, + "op": "WeakRef" + }, + { + "count": 1, + "op": "Print" + }, + { + "count": 1, + "op": "Safepoint" + } + ], + "total": 29 +} +✅ VM execution completed successfully! +Result: IntegerBox { value: 0, base: BoxBase { id: 4, parent_type_id: None } }