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:
@ -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
|
||||
|
||||
Submodule nekocode-temp updated: 509c8b7de8...9e0879ba14
@ -12,6 +12,26 @@ impl NyashInterpreter {
|
||||
/// 関数呼び出しを実行 - 🌍 革命的実装:GlobalBoxのメソッド呼び出し
|
||||
pub(super) fn execute_function_call(&mut self, name: &str, arguments: &[ASTNode])
|
||||
-> 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(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<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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,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::<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={}",
|
||||
obj_value.type_name(), obj_value.box_id());
|
||||
|
||||
|
||||
@ -43,6 +43,12 @@ pub struct MirBuilder {
|
||||
|
||||
/// Names of user-defined boxes declared in the current module
|
||||
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 {
|
||||
@ -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<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
|
||||
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<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)?;
|
||||
|
||||
// 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<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
|
||||
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<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)
|
||||
@ -941,15 +1035,38 @@ impl MirBuilder {
|
||||
fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
// 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<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の変数解決より先に行う(未定義変数で落とさない)
|
||||
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 {
|
||||
@ -1162,6 +1303,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<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)
|
||||
fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Build argument expressions
|
||||
@ -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<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
|
||||
// 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
|
||||
for (method_name, method_ast) in methods {
|
||||
if let ASTNode::FunctionDeclaration { .. } = method_ast {
|
||||
|
||||
@ -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<MirCompileResult, String> {
|
||||
// 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);
|
||||
|
||||
@ -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
|
||||
@ -51,6 +51,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::<Vec<_>>().join(",");
|
||||
format!("call_{}_{}", func.as_u32(), args_str)
|
||||
@ -254,6 +258,102 @@ impl MirOptimizer {
|
||||
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
|
||||
fn optimize_boxfield_operations(&mut self, module: &mut MirModule) -> OptimizationStats {
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
||||
27
tools/vm_stats_diff.sh
Normal file
27
tools/vm_stats_diff.sh
Normal 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
|
||||
|
||||
75
vm_stats_weak_default.json
Normal file
75
vm_stats_weak_default.json
Normal 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 } }
|
||||
65
vm_stats_weak_unified.json
Normal file
65
vm_stats_weak_unified.json
Normal 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 } }
|
||||
Reference in New Issue
Block a user