feat: MIR builder TypeOp lowering for is/as methods and isType/asType functions

- 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 <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-24 00:05:12 +09:00
parent ee740be119
commit 3c3dc86be0
10 changed files with 584 additions and 33 deletions

View File

@ -36,26 +36,26 @@
## 🚧 次にやること(再開方針) ## 🚧 次にやること(再開方針)
1) 命令セットダイエットのPoC実装(短期) 1) MIR26 前進(短期)
- 現状: VMに `TypeOp/WeakRef/Barrier` 実行経路等価とPrinter対応。Builderに補助APIを追加済未置換 - プリンタ拡張: `TypeOp/WeakRef/Barrier` `--mir-verbose` に明示表示
- 次: Builder内の該当箇所を補助APIに置換flag onで新命令を吐くoffで従来どおり - スナップショット整備: 代表ケースで 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→統合 - 旗: `mir_typeop_poc`TypeCheck/Cast→TypeOp`mir_refbarrier_unify_poc`Weak*/Barrier→統合
- 成果物: スナップショットflag on/off vm-statsのキー確認TypeOp/WeakRef/Barrier
2) VM×プラグインのE2E拡張期) 2) Builder適用拡大短期〜中期)
- HTTP: 遅延応答・大ボディの計測、到達不能時のERR安定化の再検証代表は追加済 - 言語 `is/as` 導線(最小でも擬似ノード)→ `emit_type_check/emit_cast` へ配線
- Socket: 反復タイムアウトの追加ケース(代表は追加済 - 弱参照: 既存の `RefGet/RefSet` パスは弱フィールドで `WeakLoad/WeakNew`Barrierflag ONで統合命令
- 成果物: 必要に応じてE2E追補と `VM_README.md` のTips更新
3) ResultBox単一路線への統合(中期) 3) VM/Verifierの補強(中期)
- `NyashResultBox`へ統合、旧`ResultBox`は薄いラッパーとして段階移行 - `TypeOp(Cast)` の数値キャストInt/Float安全化、誤用時TypeError整備
- 成果物: 実装整理・移行メモ・影響調査 - Verifierに26命令整合Barrier位置、WeakRef整合、支配関係チェックを追加
4) Array系の本実装必要時・中期) 4) VM×プラグインE2Eの維持期)
- VMの `ArrayGet/ArraySet` 実装済み。BoxCall fast-pathの整合性と回帰テストを充実 - HTTP/Socketの回帰確認Void防御・遅延サーバ軽量化は済
- 必要に応じて `VM_README.md` にTips追記
5) BoxCall高速化性能段階 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 cargo test -q --features plugins e2e_vm_plugin_filebox_close_void
``` ```
MIR26 PoC弱参照・Barrier統合:
```bash
# 弱フィールドPoCflag 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ダンプ/検証: MIRダンプ/検証:
```bash ```bash
nyash --dump-mir --mir-verbose examples/plugin_box_sample.nyash nyash --dump-mir --mir-verbose examples/plugin_box_sample.nyash
@ -84,7 +99,7 @@ nyash --verify examples/plugin_box_sample.nyash
- メタ降格: Debug / Nop / Safepointビルドモードで制御 - メタ降格: 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 ```bash

Submodule nekocode-temp updated: 509c8b7de8...9e0879ba14

View File

@ -12,6 +12,26 @@ impl NyashInterpreter {
/// 関数呼び出しを実行 - 🌍 革命的実装GlobalBoxのメソッド呼び出し /// 関数呼び出しを実行 - 🌍 革命的実装GlobalBoxのメソッド呼び出し
pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode]) pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode])
-> Result<Box<dyn NyashBox>, RuntimeError> { -> Result<Box<dyn NyashBox>, 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::<crate::box_trait::StringBox>() {
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(context) = self.current_constructor_context.clone() {
if let Some(parent_class) = context.parent_class { if let Some(parent_class) = context.parent_class {
@ -94,4 +114,43 @@ impl NyashInterpreter {
eprintln!("Warning: Failed to register global function: {}", err); eprintln!("Warning: Failed to register global function: {}", err);
}); });
} }
/// Helper: match a NyashBox value against a simple type name
fn matches_type_name(val: &Box<dyn NyashBox>, 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<dyn NyashBox>, type_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
match type_name {
"Integer" | "Int" | "I64" => {
// Float -> Integer (truncate), Integer -> Integer, else error
if let Some(i) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
Ok(Box::new(crate::box_trait::IntegerBox::new(i.value)))
} else if let Some(f) = val.as_any().downcast_ref::<crate::boxes::FloatBox>() {
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::<crate::boxes::FloatBox>() {
Ok(Box::new(crate::boxes::FloatBox::new(f.value)))
} else if let Some(i) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
Ok(Box::new(crate::boxes::FloatBox::new(i.value as f64)))
} else {
Ok(val)
}
}
_ => Ok(val),
}
}
} }

View File

@ -47,6 +47,22 @@ impl NyashInterpreter {
// オブジェクトを評価(通常のメソッド呼び出し) // オブジェクトを評価(通常のメソッド呼び出し)
let obj_value = self.execute_expression(object)?; 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::<crate::box_trait::StringBox>() {
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={}", eprintln!("🔍 DEBUG: execute_method_call - object evaluated to type_name='{}', box_id={}",
obj_value.type_name(), obj_value.box_id()); obj_value.type_name(), obj_value.box_id());

View File

@ -43,6 +43,12 @@ pub struct MirBuilder {
/// Names of user-defined boxes declared in the current module /// Names of user-defined boxes declared in the current module
pub(super) user_defined_boxes: HashSet<String>, pub(super) user_defined_boxes: HashSet<String>,
/// Weak field registry: BoxName -> {weak field names}
pub(super) weak_fields_by_box: HashMap<String, HashSet<String>>,
/// Remember class of object fields after assignments: (base_id, field) -> class_name
pub(super) field_origin_class: HashMap<(ValueId, String), String>,
} }
impl MirBuilder { impl MirBuilder {
@ -58,6 +64,8 @@ impl MirBuilder {
pending_phis: Vec::new(), pending_phis: Vec::new(),
value_origin_newbox: HashMap::new(), value_origin_newbox: HashMap::new(),
user_defined_boxes: HashSet::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(); let me_id = self.value_gen.next();
f.params.push(me_id); f.params.push(me_id);
self.variable_map.insert("me".to_string(), 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 // user parameters continue as %1..N
for p in &params { for p in &params {
let pid = self.value_gen.next(); let pid = self.value_gen.next();
@ -318,6 +328,17 @@ impl MirBuilder {
}, },
ASTNode::MethodCall { object, method, arguments, .. } => { 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()) self.build_method_call(*object.clone(), method.clone(), arguments.clone())
}, },
@ -338,6 +359,17 @@ impl MirBuilder {
}, },
ASTNode::FunctionCall { name, arguments, .. } => { 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()) self.build_function_call(name.clone(), arguments.clone())
}, },
@ -389,14 +421,14 @@ impl MirBuilder {
self.build_local_statement(variables.clone(), initial_values.clone()) 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" { if is_static && name == "Main" {
self.build_static_main_box(methods.clone()) self.build_static_main_box(methods.clone())
} else { } else {
// Support user-defined boxes - handle as statement, return void // Support user-defined boxes - handle as statement, return void
// Track as user-defined (eligible for method lowering) // Track as user-defined (eligible for method lowering)
self.user_defined_boxes.insert(name.clone()); 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 // Phase 2: Lower constructors (birth/N) into MIR functions
// Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1") // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1")
@ -535,6 +567,17 @@ impl MirBuilder {
/// Build function call /// Build function call
fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> { fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
// 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 // Build argument values
let mut arg_values = Vec::new(); let mut arg_values = Vec::new();
for arg in args { for arg in args {
@ -563,6 +606,24 @@ impl MirBuilder {
/// Build print statement - converts to console output /// Build print statement - converts to console output
fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> { fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
// 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)?; let value = self.build_expression(expression)?;
// For now, use a special Print instruction (minimal scope) // For now, use a special Print instruction (minimal scope)
@ -885,18 +946,51 @@ impl MirBuilder {
/// Build field access: object.field /// Build field access: object.field
fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> { fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
// 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 // First, build the object expression to get its ValueId
let object_value = self.build_expression(object)?; let object_value = self.build_expression(object)?;
// Get the field from the object using RefGet // 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 { self.emit_instruction(MirInstruction::RefGet {
dst: result_id, dst: field_val,
reference: object_value, 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<String> = 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) /// 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<ValueId, String> { fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
// Build the object and value expressions // Build the object and value expressions
let object_value = self.build_expression(object)?; 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 // Set the field using RefSet
self.emit_instruction(MirInstruction::RefSet { self.emit_instruction(MirInstruction::RefSet {
reference: object_value, reference: object_value,
field, field: field.clone(),
value: value_result, 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 // Return the assigned value
Ok(value_result) Ok(value_result)
} }
@ -1059,6 +1176,19 @@ impl MirBuilder {
/// Build method call: object.method(arguments) /// Build method call: object.method(arguments)
fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> { fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// 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の変数解決より先に行う未定義変数で落とさない // ExternCall判定はobjectの変数解決より先に行う未定義変数で落とさない
if let ASTNode::Variable { name: object_name, .. } = object.clone() { if let ASTNode::Variable { name: object_name, .. } = object.clone() {
// Build argument expressions first (externはobject自体を使わない) // Build argument expressions first (externはobject自体を使わない)
@ -1098,6 +1228,17 @@ impl MirBuilder {
// Build the object expression // Build the object expression
let object_value = self.build_expression(object.clone())?; 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 // Build argument expressions
let mut arg_values = Vec::new(); let mut arg_values = Vec::new();
for arg in &arguments { for arg in &arguments {
@ -1162,6 +1303,35 @@ impl MirBuilder {
Ok(result_id) 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<String> {
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) /// Build from expression: from Parent.method(arguments)
fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> { fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// Build argument expressions // Build argument expressions
@ -1193,7 +1363,7 @@ impl MirBuilder {
} }
/// Build box declaration: box Name { fields... methods... } /// Build box declaration: box Name { fields... methods... }
fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap<String, ASTNode>, fields: Vec<String>) -> Result<(), String> { fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
// For Phase 8.4, we'll emit metadata instructions to register the box type // 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 // In a full implementation, this would register type information for later use
@ -1213,6 +1383,12 @@ impl MirBuilder {
})?; })?;
} }
// Record weak fields for this box
if !weak_fields.is_empty() {
let set: HashSet<String> = weak_fields.into_iter().collect();
self.weak_fields_by_box.insert(name.clone(), set);
}
// Process methods - now methods is a HashMap // Process methods - now methods is a HashMap
for (method_name, method_ast) in methods { for (method_name, method_ast) in methods {
if let ASTNode::FunctionDeclaration { .. } = method_ast { if let ASTNode::FunctionDeclaration { .. } = method_ast {

View File

@ -16,6 +16,7 @@ pub mod ownership_verifier_simple; // Simple ownership forest verification for c
pub mod printer; pub mod printer;
pub mod value_id; pub mod value_id;
pub mod effect; pub mod effect;
pub mod optimizer;
// Re-export main types for easy access // Re-export main types for easy access
pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp}; 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 printer::MirPrinter;
pub use value_id::{ValueId, LocalId, ValueIdGenerator}; pub use value_id::{ValueId, LocalId, ValueIdGenerator};
pub use effect::{EffectMask, Effect}; pub use effect::{EffectMask, Effect};
pub use optimizer::MirOptimizer;
/// MIR compilation result /// MIR compilation result
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -54,7 +56,11 @@ impl MirCompiler {
/// Compile AST to MIR module with verification /// Compile AST to MIR module with verification
pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result<MirCompileResult, String> { pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result<MirCompileResult, String> {
// Convert AST to MIR using builder // 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 // Verify the generated MIR
let verification_result = self.verifier.verify_module(&module); let verification_result = self.verifier.verify_module(&module);

View File

@ -8,7 +8,7 @@
* - Dead code elimination * - Dead code elimination
*/ */
use super::{MirModule, MirFunction, MirInstruction, ValueId, EffectMask, Effect}; use super::{MirModule, MirFunction, MirInstruction, ValueId, MirType, TypeOpKind};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
/// MIR optimization passes /// MIR optimization passes
@ -51,6 +51,9 @@ impl MirOptimizer {
// Pass 4: Intrinsic function optimization // Pass 4: Intrinsic function optimization
stats.merge(self.optimize_intrinsic_calls(module)); 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 // Pass 5: BoxField dependency optimization
stats.merge(self.optimize_boxfield_operations(module)); stats.merge(self.optimize_boxfield_operations(module));
@ -196,7 +199,8 @@ impl MirOptimizer {
MirInstruction::Const { value, .. } => format!("const_{:?}", value), MirInstruction::Const { value, .. } => format!("const_{:?}", value),
MirInstruction::BinOp { op, lhs, rhs, .. } => format!("binop_{:?}_{}_{}", op, lhs.as_u32(), rhs.as_u32()), 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::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, .. } => { MirInstruction::Call { func, args, .. } => {
let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::<Vec<_>>().join(","); let args_str = args.iter().map(|v| v.as_u32().to_string()).collect::<Vec<_>>().join(",");
format!("call_{}_{}", func.as_u32(), args_str) format!("call_{}_{}", func.as_u32(), args_str)
@ -254,6 +258,102 @@ impl MirOptimizer {
0 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<ValueId, (super::basic_block::BasicBlockId, usize)> = 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 /// Optimize BoxField operations
fn optimize_boxfield_operations(&mut self, module: &mut MirModule) -> OptimizationStats { fn optimize_boxfield_operations(&mut self, module: &mut MirModule) -> OptimizationStats {
let mut stats = OptimizationStats::new(); let mut stats = OptimizationStats::new();
@ -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 { impl Default for MirOptimizer {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()

27
tools/vm_stats_diff.sh Normal file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
if [ $# -ne 2 ]; then
echo "Usage: $0 <stats_before.json> <stats_after.json>" >&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

View File

@ -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 } }

View File

@ -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 } }