bridge/json_v0: split expr lowering; add Ternary/Peek lowering + AST; selfhost Peek JSON emit; add selfhost Peek smoke; warning cleanup in lowering/optimizer/verification
- Split expr lowering into ; route calls from stmt lowering
- Implement ternary/peek lowering (MIR13 PHI-off=Copy, PHI-on=Phi)
- Extend JSON v0 AST (ExprV0::{Ternary,Peek}, PeekArmV0)
- Selfhost parser_box: emit Peek JSON; add Stage-2 'Peek basic' smoke
- Reduce warnings: remove unused imports/vars in several modules
- current_task: update plan for legacy VM/Interpreter offboarding
This commit is contained in:
@ -9,13 +9,17 @@ What Changed (recent)
|
|||||||
- `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`).
|
- `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`).
|
||||||
- Curated LLVM runner defaults to PHI‑off; `--phi-on` enables MIR14 lane.
|
- Curated LLVM runner defaults to PHI‑off; `--phi-on` enables MIR14 lane.
|
||||||
- Added doc: `docs/development/mir/MIR13_MODE.md`; README references it.
|
- Added doc: `docs/development/mir/MIR13_MODE.md`; README references it.
|
||||||
- JSON v0 Bridge lowering split (non‑functional)
|
- JSON v0 Bridge lowering refactor + features
|
||||||
- Added `src/runner/json_v0_bridge/lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs}` and routed calls from `lowering.rs`.
|
- Split helpers: `src/runner/json_v0_bridge/lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs}`(既存)に加え、式系を `lowering/expr.rs` に分離(振る舞い不変)。
|
||||||
|
- 新規サポート: Ternary/Peek の Lowering を実装し、`expr.rs` から `ternary.rs`/`peek.rs` へ委譲(MIR13 PHI‑off=Copy合流/PHI‑on=Phi 合流)。
|
||||||
|
- Self‑host 生成器(Stage‑1 JSON v0)に Peek emit を追加: `apps/selfhost-compiler/boxes/parser_box.nyash`。
|
||||||
|
- Selfhost/PyVM スモークを通して E2E 確認(peek/ternary)。
|
||||||
- llvmlite stability for MIR13
|
- llvmlite stability for MIR13
|
||||||
- Resolver: forbids cross‑block non‑dominating vmap reuse; for multi‑pred and no declared PHI, synthesizes a localization PHI at block head.
|
- Resolver: forbids cross‑block non‑dominating vmap reuse; for multi‑pred and no declared PHI, synthesizes a localization PHI at block head.
|
||||||
- Finalize remains function‑local; `block_end_values` snapshots and placeholder wiring still in place.
|
- Finalize remains function‑local; `block_end_values` snapshots and placeholder wiring still in place.
|
||||||
- Parity runner pragmatics
|
- Parity runner pragmatics
|
||||||
- `tools/pyvm_vs_llvmlite.sh` compares exit code by default; use `CMP_STRICT=1` for stdout+exit.
|
- `tools/pyvm_vs_llvmlite.sh` compares exit code by default; use `CMP_STRICT=1` for stdout+exit.
|
||||||
|
- Stage‑2 smokes更新: `tools/selfhost_stage2_smoke.sh` に "Peek basic" を追加。
|
||||||
|
|
||||||
Current Status
|
Current Status
|
||||||
- Self‑hosting Bridge → PyVM smokes: PASS (Stage‑2 reps: array/string/logic/if/loop).
|
- Self‑hosting Bridge → PyVM smokes: PASS (Stage‑2 reps: array/string/logic/if/loop).
|
||||||
@ -23,11 +27,16 @@ Current Status
|
|||||||
- Curated LLVM (PHI‑on experimental): `apps/tests/loop_if_phi.nyash` shows a dominance issue (observed; not blocking, MIR13 recommended).
|
- Curated LLVM (PHI‑on experimental): `apps/tests/loop_if_phi.nyash` shows a dominance issue (observed; not blocking, MIR13 recommended).
|
||||||
|
|
||||||
Next (short plan)
|
Next (short plan)
|
||||||
1) JSON v0 lowering: split remaining helpers (peek/ternary/expr) without behavior change.
|
1) Legacy Interpreter/VM offboarding (phase‑A):
|
||||||
2) PHI‑on lane (optional): investigate `loop_if_phi` dominance by tightening finalize ordering and resolver materialization (low priority).
|
- Introduce `vm-legacy` feature (default OFF) to gate old VM execution層。
|
||||||
3) Runner refactor (small PRs):
|
- 抽出: JIT が参照する最小型(例: `VMValue`)を薄い共通モジュールへ切替(`vm_types` 等)。
|
||||||
- `selfhost/{child.rs,json.rs}` split; `modes/common/{io,resolve,exec}.rs` split; reduce `runner/mod.rs` surface.
|
- `interpreter-legacy`/`vm-legacy` を既定ビルドから外し、ビルド警告を縮小。
|
||||||
4) Optimizer/Verifier thin‑hub cleanup (non‑functional): orchestrator minimalization and pass boundaries clarified.
|
2) Legacy Interpreter/VM offboarding (phase‑B):
|
||||||
|
- 物理移動: `src/archive/{interpreter_legacy,vm_legacy}/` へ移設(ドキュメント更新)。
|
||||||
|
3) PHI‑on lane(任意): `loop_if_phi` 支配関係を finalize/resolve の順序強化で観察(低優先)。
|
||||||
|
4) Runner refactor(小PR):
|
||||||
|
- `selfhost/{child.rs,json.rs}` 分離; `modes/common/{io,resolve,exec}.rs` 分割; `runner/mod.rs`の表面削減。
|
||||||
|
5) Optimizer/Verifier thin‑hub cleanup(非機能): orchestrator最小化とパス境界の明確化。
|
||||||
|
|
||||||
How to Run
|
How to Run
|
||||||
- PyVM reference smokes: `tools/pyvm_stage2_smoke.sh`
|
- PyVM reference smokes: `tools/pyvm_stage2_smoke.sh`
|
||||||
@ -46,4 +55,4 @@ Notes / Policies
|
|||||||
- Focus is self‑hosting stability. JIT/Cranelift is out of scope (safety fixes only).
|
- Focus is self‑hosting stability. JIT/Cranelift is out of scope (safety fixes only).
|
||||||
- PHI generation remains centralized in llvmlite; Bridge/Builder keep PHI‑off by default.
|
- PHI generation remains centralized in llvmlite; Bridge/Builder keep PHI‑off by default.
|
||||||
- No full tracing GC yet; handles/Arc lifetimes govern object retention. Safepoint/barrier/roots are staging utilities.
|
- No full tracing GC yet; handles/Arc lifetimes govern object retention. Safepoint/barrier/roots are staging utilities.
|
||||||
|
- Legacy Interpreter/VM は段階的にアーカイブへ。日常の意味論確認は PyVM を基準として継続。
|
||||||
|
|||||||
@ -247,6 +247,86 @@ box ParserBox {
|
|||||||
if me.starts_with_kw(src, j, "true") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Bool\",\"value\":true}" }
|
if me.starts_with_kw(src, j, "true") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Bool\",\"value\":true}" }
|
||||||
if me.starts_with_kw(src, j, "false") == 1 { me.gpos_set(j + 5) return "{\"type\":\"Bool\",\"value\":false}" }
|
if me.starts_with_kw(src, j, "false") == 1 { me.gpos_set(j + 5) return "{\"type\":\"Bool\",\"value\":false}" }
|
||||||
if me.starts_with_kw(src, j, "null") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Null\"}" }
|
if me.starts_with_kw(src, j, "null") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Null\"}" }
|
||||||
|
// Peek expression: peek <expr> { "label" => <expr>, ..., else => <expr> }
|
||||||
|
if me.starts_with_kw(src, j, "peek") == 1 {
|
||||||
|
j = j + 4
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
// scrutinee expression
|
||||||
|
local scr = me.parse_expr2(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if src.substring(j, j+1) == "{" { j = j + 1 } // enter arms block
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
local arms_json = "["
|
||||||
|
local first_arm = 1
|
||||||
|
local else_json = null
|
||||||
|
local n = src.length()
|
||||||
|
local contp = 1
|
||||||
|
local guardp = 0
|
||||||
|
local maxp = 400000
|
||||||
|
loop(contp == 1) {
|
||||||
|
if guardp > maxp { contp = 0 } else { guardp = guardp + 1 }
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if j >= n { contp = 0 } else {
|
||||||
|
if src.substring(j, j+1) == "}" {
|
||||||
|
j = j + 1
|
||||||
|
contp = 0
|
||||||
|
} else {
|
||||||
|
// else arm or labeled arm
|
||||||
|
if me.starts_with_kw(src, j, "else") == 1 {
|
||||||
|
j = j + 4
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if src.substring(j, j+2) == "=>" { j = j + 2 }
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
// else body may be a block or bare expr
|
||||||
|
if src.substring(j, j+1) == "{" {
|
||||||
|
j = j + 1
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
else_json = me.parse_expr2(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if src.substring(j, j+1) == "}" { j = j + 1 }
|
||||||
|
} else {
|
||||||
|
else_json = me.parse_expr2(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
}
|
||||||
|
// optional separator/newline tolerated; continue until '}'
|
||||||
|
} else {
|
||||||
|
// labeled arm: string literal label
|
||||||
|
if src.substring(j, j+1) != "\"" {
|
||||||
|
// degrade safely to avoid infinite loop
|
||||||
|
j = j + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
local label_raw = me.read_string_lit(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if src.substring(j, j+2) == "=>" { j = j + 2 }
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
// arm expr: block or bare expr
|
||||||
|
local expr_json = "{\"type\":\"Int\",\"value\":0}"
|
||||||
|
if src.substring(j, j+1) == "{" {
|
||||||
|
j = j + 1
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
expr_json = me.parse_expr2(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
j = me.skip_ws(src, j)
|
||||||
|
if src.substring(j, j+1) == "}" { j = j + 1 }
|
||||||
|
} else {
|
||||||
|
expr_json = me.parse_expr2(src, j)
|
||||||
|
j = me.gpos_get()
|
||||||
|
}
|
||||||
|
local arm_json = "{\"label\":\"" + me.esc_json(label_raw) + "\",\"expr\":" + expr_json + "}"
|
||||||
|
if first_arm == 1 { arms_json = arms_json + arm_json first_arm = 0 } else { arms_json = arms_json + "," + arm_json }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arms_json = arms_json + "]"
|
||||||
|
if else_json == null { else_json = "{\"type\":\"Null\"}" }
|
||||||
|
me.gpos_set(j)
|
||||||
|
return "{\"type\":\"Peek\",\"scrutinee\":" + scr + ",\"arms\":" + arms_json + ",\"else\":" + else_json + "}"
|
||||||
|
}
|
||||||
local ch = src.substring(j, j+1)
|
local ch = src.substring(j, j+1)
|
||||||
// Parenthesized
|
// Parenthesized
|
||||||
if ch == "(" {
|
if ch == "(" {
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
use super::{
|
use super::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId};
|
||||||
BasicBlockIdGenerator, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule,
|
|
||||||
MirType, ValueId,
|
|
||||||
};
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
// Lifecycle routines extracted from builder.rs
|
// Lifecycle routines extracted from builder.rs
|
||||||
@ -106,4 +103,3 @@ impl super::MirBuilder {
|
|||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::mir::optimizer::MirOptimizer;
|
use crate::mir::optimizer::MirOptimizer;
|
||||||
use crate::mir::optimizer_stats::OptimizationStats;
|
use crate::mir::optimizer_stats::OptimizationStats;
|
||||||
use crate::mir::{EffectMask, MirInstruction as I, MirModule};
|
use crate::mir::{MirInstruction as I, MirModule};
|
||||||
|
|
||||||
/// Optimize BoxField operations (scaffolding)
|
/// Optimize BoxField operations (scaffolding)
|
||||||
pub fn optimize_boxfield_operations(
|
pub fn optimize_boxfield_operations(
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::mir::optimizer::MirOptimizer;
|
use crate::mir::optimizer::MirOptimizer;
|
||||||
use crate::mir::optimizer_stats::OptimizationStats;
|
use crate::mir::optimizer_stats::OptimizationStats;
|
||||||
use crate::mir::{function::MirFunction, BasicBlockId, MirInstruction, MirModule, ValueId};
|
use crate::mir::{BasicBlockId, MirInstruction, MirModule, ValueId};
|
||||||
|
|
||||||
/// Diagnostic: detect unlowered is/as/isType/asType after Builder
|
/// Diagnostic: detect unlowered is/as/isType/asType after Builder
|
||||||
pub fn diagnose_unlowered_type_ops(
|
pub fn diagnose_unlowered_type_ops(
|
||||||
|
|||||||
@ -9,7 +9,7 @@ pub fn optimize_intrinsic_calls(
|
|||||||
opt: &mut MirOptimizer,
|
opt: &mut MirOptimizer,
|
||||||
module: &mut MirModule,
|
module: &mut MirModule,
|
||||||
) -> OptimizationStats {
|
) -> OptimizationStats {
|
||||||
let mut stats = OptimizationStats::new();
|
let stats = OptimizationStats::new();
|
||||||
for (func_name, _function) in &mut module.functions {
|
for (func_name, _function) in &mut module.functions {
|
||||||
if opt.debug_enabled() {
|
if opt.debug_enabled() {
|
||||||
println!(" ⚡ Intrinsic optimization in function: {}", func_name);
|
println!(" ⚡ Intrinsic optimization in function: {}", func_name);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::mir::optimizer::MirOptimizer;
|
use crate::mir::optimizer::MirOptimizer;
|
||||||
use crate::mir::optimizer_stats::OptimizationStats;
|
use crate::mir::optimizer_stats::OptimizationStats;
|
||||||
use crate::mir::{BarrierOp, MirInstruction, MirModule, TypeOpKind, ValueId, WeakRefOp};
|
use crate::mir::{BarrierOp, MirModule, TypeOpKind, ValueId, WeakRefOp};
|
||||||
|
|
||||||
pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats {
|
pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats {
|
||||||
use crate::mir::MirInstruction as I;
|
use crate::mir::MirInstruction as I;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ pub fn reorder_pure_instructions(
|
|||||||
opt: &mut MirOptimizer,
|
opt: &mut MirOptimizer,
|
||||||
module: &mut MirModule,
|
module: &mut MirModule,
|
||||||
) -> OptimizationStats {
|
) -> OptimizationStats {
|
||||||
let mut stats = OptimizationStats::new();
|
let stats = OptimizationStats::new();
|
||||||
for (func_name, _function) in &mut module.functions {
|
for (func_name, _function) in &mut module.functions {
|
||||||
if opt.debug_enabled() {
|
if opt.debug_enabled() {
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Extracted from the monolithic optimizer to enable modular pass composition.
|
//! Extracted from the monolithic optimizer to enable modular pass composition.
|
||||||
|
|
||||||
use crate::mir::{MirFunction, MirInstruction, MirModule, ValueId};
|
use crate::mir::{MirFunction, MirModule, ValueId};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
/// Eliminate dead code (unused results of pure instructions) across the module.
|
/// Eliminate dead code (unused results of pure instructions) across the module.
|
||||||
@ -59,7 +59,7 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize {
|
|||||||
|
|
||||||
// Remove unused pure instructions
|
// Remove unused pure instructions
|
||||||
let mut eliminated = 0usize;
|
let mut eliminated = 0usize;
|
||||||
for (bbid, block) in &mut function.blocks {
|
for (_bbid, block) in &mut function.blocks {
|
||||||
block.instructions.retain(|inst| {
|
block.instructions.retain(|inst| {
|
||||||
if inst.effects().is_pure() {
|
if inst.effects().is_pure() {
|
||||||
if let Some(dst) = inst.dst_value() {
|
if let Some(dst) = inst.dst_value() {
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
use super::{BasicBlockId, MirFunction, MirModule, ValueId};
|
use super::{BasicBlockId, MirFunction, MirModule, ValueId};
|
||||||
use crate::debug::log as dlog;
|
use crate::debug::log as dlog;
|
||||||
use crate::mir::verification_types::VerificationError;
|
use crate::mir::verification_types::VerificationError;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
mod awaits;
|
mod awaits;
|
||||||
mod barrier;
|
mod barrier;
|
||||||
mod legacy;
|
mod legacy;
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use super::common::ParserUtils;
|
use super::common::ParserUtils;
|
||||||
use super::{NyashParser, ParseError};
|
use super::{NyashParser, ParseError};
|
||||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span, UnaryOperator};
|
use crate::ast::{ASTNode, Span, UnaryOperator};
|
||||||
use crate::tokenizer::TokenType;
|
use crate::tokenizer::TokenType;
|
||||||
|
|
||||||
// Debug macros are now imported from the parent module via #[macro_export]
|
// Debug macros are now imported from the parent module via #[macro_export]
|
||||||
|
|||||||
@ -57,6 +57,12 @@ pub(super) struct CatchV0 {
|
|||||||
pub(super) body: Vec<StmtV0>,
|
pub(super) body: Vec<StmtV0>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub(super) struct PeekArmV0 {
|
||||||
|
pub(super) label: String,
|
||||||
|
pub(super) expr: ExprV0,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub(super) enum ExprV0 {
|
pub(super) enum ExprV0 {
|
||||||
@ -108,4 +114,16 @@ pub(super) enum ExprV0 {
|
|||||||
Throw {
|
Throw {
|
||||||
expr: Box<ExprV0>,
|
expr: Box<ExprV0>,
|
||||||
},
|
},
|
||||||
|
Ternary {
|
||||||
|
cond: Box<ExprV0>,
|
||||||
|
then: Box<ExprV0>,
|
||||||
|
#[serde(rename = "else")]
|
||||||
|
r#else: Box<ExprV0>,
|
||||||
|
},
|
||||||
|
Peek {
|
||||||
|
scrutinee: Box<ExprV0>,
|
||||||
|
arms: Vec<PeekArmV0>,
|
||||||
|
#[serde(rename = "else")]
|
||||||
|
r#else: Box<ExprV0>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
use super::ast::{CatchV0, ExprV0, ProgramV0, StmtV0};
|
use super::ast::{ProgramV0, StmtV0};
|
||||||
use crate::mir::{
|
use crate::mir::{
|
||||||
BasicBlock, BasicBlockId, BinaryOp, ConstValue, EffectMask, FunctionSignature, MirFunction,
|
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule,
|
||||||
MirInstruction, MirModule, MirPrinter, MirType, ValueId,
|
MirPrinter, MirType, ValueId,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
// Split out merge/new_block helpers for readability (no behavior change)
|
// Split out merge/new_block helpers for readability (no behavior change)
|
||||||
mod merge;
|
mod merge;
|
||||||
use merge::{merge_var_maps, merge_values, new_block};
|
use merge::{merge_var_maps, new_block};
|
||||||
// Feature splits (gradual extraction)
|
// Feature splits (gradual extraction)
|
||||||
pub(super) mod if_else;
|
pub(super) mod if_else;
|
||||||
pub(super) mod loop_;
|
pub(super) mod loop_;
|
||||||
pub(super) mod try_catch;
|
pub(super) mod try_catch;
|
||||||
|
pub(super) mod expr;
|
||||||
|
pub(super) mod ternary; // placeholder (not wired)
|
||||||
|
pub(super) mod peek; // placeholder (not wired)
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub(super) struct LoopContext {
|
pub(super) struct LoopContext {
|
||||||
@ -39,472 +41,6 @@ impl BridgeEnv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait VarScope {
|
|
||||||
fn resolve(
|
|
||||||
&mut self,
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<Option<ValueId>, String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NoVars;
|
|
||||||
impl VarScope for NoVars {
|
|
||||||
fn resolve(
|
|
||||||
&mut self,
|
|
||||||
_env: &BridgeEnv,
|
|
||||||
_f: &mut MirFunction,
|
|
||||||
_cur_bb: BasicBlockId,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<Option<ValueId>, String> {
|
|
||||||
Err(format!("undefined variable in this context: {}", name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MapVars<'a> {
|
|
||||||
vars: &'a mut HashMap<String, ValueId>,
|
|
||||||
}
|
|
||||||
impl<'a> MapVars<'a> {
|
|
||||||
fn new(vars: &'a mut HashMap<String, ValueId>) -> Self {
|
|
||||||
Self { vars }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> VarScope for MapVars<'a> {
|
|
||||||
fn resolve(
|
|
||||||
&mut self,
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<Option<ValueId>, String> {
|
|
||||||
if let Some(&vid) = self.vars.get(name) {
|
|
||||||
return Ok(Some(vid));
|
|
||||||
}
|
|
||||||
if name == "me" {
|
|
||||||
if env.allow_me_dummy {
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::NewBox {
|
|
||||||
dst,
|
|
||||||
box_type: env.me_class.clone(),
|
|
||||||
args: vec![],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.vars.insert(name.to_string(), dst);
|
|
||||||
Ok(Some(dst))
|
|
||||||
} else {
|
|
||||||
Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// moved helpers are imported above
|
|
||||||
|
|
||||||
fn lower_throw(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
exception_value: ValueId,
|
|
||||||
) -> (ValueId, BasicBlockId) {
|
|
||||||
if env.throw_enabled {
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.set_terminator(MirInstruction::Throw {
|
|
||||||
exception: exception_value,
|
|
||||||
effects: EffectMask::PANIC,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(exception_value, cur_bb)
|
|
||||||
} else {
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst,
|
|
||||||
value: ConstValue::Integer(0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(dst, cur_bb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lower_expr_with_scope<S: VarScope>(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
e: &ExprV0,
|
|
||||||
vars: &mut S,
|
|
||||||
) -> Result<(ValueId, BasicBlockId), String> {
|
|
||||||
match e {
|
|
||||||
ExprV0::Int { value } => {
|
|
||||||
let ival: i64 = if let Some(n) = value.as_i64() {
|
|
||||||
n
|
|
||||||
} else if let Some(s) = value.as_str() {
|
|
||||||
s.parse().map_err(|_| "invalid int literal")?
|
|
||||||
} else {
|
|
||||||
return Err("invalid int literal".into());
|
|
||||||
};
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst,
|
|
||||||
value: ConstValue::Integer(ival),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur_bb))
|
|
||||||
}
|
|
||||||
ExprV0::Str { value } => {
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst,
|
|
||||||
value: ConstValue::String(value.clone()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur_bb))
|
|
||||||
}
|
|
||||||
ExprV0::Bool { value } => {
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst,
|
|
||||||
value: ConstValue::Bool(*value),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur_bb))
|
|
||||||
}
|
|
||||||
ExprV0::Binary { op, lhs, rhs } => {
|
|
||||||
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
|
||||||
let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
|
|
||||||
let bop = match op.as_str() {
|
|
||||||
"+" => BinaryOp::Add,
|
|
||||||
"-" => BinaryOp::Sub,
|
|
||||||
"*" => BinaryOp::Mul,
|
|
||||||
"/" => BinaryOp::Div,
|
|
||||||
_ => return Err("unsupported op".into()),
|
|
||||||
};
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
|
||||||
bb.add_instruction(MirInstruction::BinOp {
|
|
||||||
dst,
|
|
||||||
op: bop,
|
|
||||||
lhs: l,
|
|
||||||
rhs: r,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur_after_r))
|
|
||||||
}
|
|
||||||
ExprV0::Extern {
|
|
||||||
iface,
|
|
||||||
method,
|
|
||||||
args,
|
|
||||||
} => {
|
|
||||||
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur2) {
|
|
||||||
bb.add_instruction(MirInstruction::ExternCall {
|
|
||||||
dst: Some(dst),
|
|
||||||
iface_name: iface.clone(),
|
|
||||||
method_name: method.clone(),
|
|
||||||
args: arg_ids,
|
|
||||||
effects: EffectMask::IO,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur2))
|
|
||||||
}
|
|
||||||
ExprV0::Compare { op, lhs, rhs } => {
|
|
||||||
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
|
||||||
let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
|
|
||||||
let cop = match op.as_str() {
|
|
||||||
"==" => crate::mir::CompareOp::Eq,
|
|
||||||
"!=" => crate::mir::CompareOp::Ne,
|
|
||||||
"<" => crate::mir::CompareOp::Lt,
|
|
||||||
"<=" => crate::mir::CompareOp::Le,
|
|
||||||
">" => crate::mir::CompareOp::Gt,
|
|
||||||
">=" => crate::mir::CompareOp::Ge,
|
|
||||||
_ => return Err("unsupported compare op".into()),
|
|
||||||
};
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
|
||||||
bb.add_instruction(MirInstruction::Compare {
|
|
||||||
dst,
|
|
||||||
op: cop,
|
|
||||||
lhs: l,
|
|
||||||
rhs: r,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur_after_r))
|
|
||||||
}
|
|
||||||
ExprV0::Logical { op, lhs, rhs } => {
|
|
||||||
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
|
||||||
let rhs_bb = new_block(f);
|
|
||||||
let fall_bb = new_block(f);
|
|
||||||
let merge_bb = new_block(f);
|
|
||||||
let is_and = matches!(op.as_str(), "&&" | "and");
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_after_l) {
|
|
||||||
if is_and {
|
|
||||||
bb.set_terminator(MirInstruction::Branch {
|
|
||||||
condition: l,
|
|
||||||
then_bb: rhs_bb,
|
|
||||||
else_bb: fall_bb,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
bb.set_terminator(MirInstruction::Branch {
|
|
||||||
condition: l,
|
|
||||||
then_bb: fall_bb,
|
|
||||||
else_bb: rhs_bb,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::jit::events::emit_lower(
|
|
||||||
serde_json::json!({ "id":"shortcircuit","op": if is_and {"and"} else {"or"},"rhs_bb":rhs_bb.0,"fall_bb":fall_bb.0,"merge_bb":merge_bb.0 }),
|
|
||||||
"shortcircuit",
|
|
||||||
"<json_v0>",
|
|
||||||
);
|
|
||||||
let cdst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(fall_bb) {
|
|
||||||
let cval = if is_and {
|
|
||||||
ConstValue::Bool(false)
|
|
||||||
} else {
|
|
||||||
ConstValue::Bool(true)
|
|
||||||
};
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst: cdst,
|
|
||||||
value: cval,
|
|
||||||
});
|
|
||||||
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
||||||
}
|
|
||||||
let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?;
|
|
||||||
if let Some(bb) = f.get_block_mut(rhs_end) {
|
|
||||||
if !bb.is_terminated() {
|
|
||||||
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let out = f.next_value_id();
|
|
||||||
if env.mir_no_phi {
|
|
||||||
if let Some(bb) = f.get_block_mut(fall_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::Copy {
|
|
||||||
dst: out,
|
|
||||||
src: cdst,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if let Some(bb) = f.get_block_mut(rhs_end) {
|
|
||||||
bb.add_instruction(MirInstruction::Copy {
|
|
||||||
dst: out,
|
|
||||||
src: rval,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
|
||||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
|
|
||||||
if rhs_end != fall_bb {
|
|
||||||
inputs.push((rhs_end, rval));
|
|
||||||
} else {
|
|
||||||
inputs.push((fall_bb, rval));
|
|
||||||
}
|
|
||||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
|
||||||
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
|
|
||||||
}
|
|
||||||
Ok((out, merge_bb))
|
|
||||||
}
|
|
||||||
ExprV0::Call { name, args } => {
|
|
||||||
if name == "array.of" {
|
|
||||||
let arr = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::NewBox {
|
|
||||||
dst: arr,
|
|
||||||
box_type: "ArrayBox".into(),
|
|
||||||
args: vec![],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut cur = cur_bb;
|
|
||||||
for e in args {
|
|
||||||
let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?;
|
|
||||||
cur = c;
|
|
||||||
let tmp = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
|
||||||
bb.add_instruction(MirInstruction::BoxCall {
|
|
||||||
dst: Some(tmp),
|
|
||||||
box_val: arr,
|
|
||||||
method: "push".into(),
|
|
||||||
method_id: None,
|
|
||||||
args: vec![v],
|
|
||||||
effects: EffectMask::READ,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok((arr, cur));
|
|
||||||
}
|
|
||||||
if name == "map.of" {
|
|
||||||
let mapv = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
||||||
bb.add_instruction(MirInstruction::NewBox {
|
|
||||||
dst: mapv,
|
|
||||||
box_type: "MapBox".into(),
|
|
||||||
args: vec![],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut cur = cur_bb;
|
|
||||||
let mut it = args.iter();
|
|
||||||
while let Some(k) = it.next() {
|
|
||||||
if let Some(v) = it.next() {
|
|
||||||
let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?;
|
|
||||||
cur = cur2;
|
|
||||||
let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?;
|
|
||||||
cur = cur3;
|
|
||||||
let tmp = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
|
||||||
bb.add_instruction(MirInstruction::BoxCall {
|
|
||||||
dst: Some(tmp),
|
|
||||||
box_val: mapv,
|
|
||||||
method: "set".into(),
|
|
||||||
method_id: None,
|
|
||||||
args: vec![kv, vv],
|
|
||||||
effects: EffectMask::READ,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok((mapv, cur));
|
|
||||||
}
|
|
||||||
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
|
||||||
let fun_val = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
|
||||||
bb.add_instruction(MirInstruction::Const {
|
|
||||||
dst: fun_val,
|
|
||||||
value: ConstValue::String(name.clone()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
|
||||||
bb.add_instruction(MirInstruction::Call {
|
|
||||||
dst: Some(dst),
|
|
||||||
func: fun_val,
|
|
||||||
args: arg_ids,
|
|
||||||
effects: EffectMask::READ,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur))
|
|
||||||
}
|
|
||||||
ExprV0::Method { recv, method, args } => {
|
|
||||||
let recv_is_console_new =
|
|
||||||
matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
|
||||||
if recv_is_console_new && (method == "println" || method == "print" || method == "log")
|
|
||||||
{
|
|
||||||
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur2) {
|
|
||||||
bb.add_instruction(MirInstruction::ExternCall {
|
|
||||||
dst: Some(dst),
|
|
||||||
iface_name: "env.console".into(),
|
|
||||||
method_name: "log".into(),
|
|
||||||
args: arg_ids,
|
|
||||||
effects: EffectMask::READ,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Ok((dst, cur2));
|
|
||||||
}
|
|
||||||
let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?;
|
|
||||||
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?;
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur2) {
|
|
||||||
bb.add_instruction(MirInstruction::BoxCall {
|
|
||||||
dst: Some(dst),
|
|
||||||
box_val: recv_v,
|
|
||||||
method: method.clone(),
|
|
||||||
method_id: None,
|
|
||||||
args: arg_ids,
|
|
||||||
effects: EffectMask::READ,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur2))
|
|
||||||
}
|
|
||||||
ExprV0::New { class, args } => {
|
|
||||||
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
|
||||||
let dst = f.next_value_id();
|
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
|
||||||
bb.add_instruction(MirInstruction::NewBox {
|
|
||||||
dst,
|
|
||||||
box_type: class.clone(),
|
|
||||||
args: arg_ids,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok((dst, cur))
|
|
||||||
}
|
|
||||||
ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? {
|
|
||||||
Some(v) => Ok((v, cur_bb)),
|
|
||||||
None => Err(format!("undefined variable: {}", name)),
|
|
||||||
},
|
|
||||||
ExprV0::Throw { expr } => {
|
|
||||||
let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?;
|
|
||||||
Ok(lower_throw(env, f, cur, exc))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lower_args_with_scope<S: VarScope>(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
args: &[ExprV0],
|
|
||||||
scope: &mut S,
|
|
||||||
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
|
||||||
let mut out = Vec::with_capacity(args.len());
|
|
||||||
let mut cur = cur_bb;
|
|
||||||
for a in args {
|
|
||||||
let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?;
|
|
||||||
out.push(v);
|
|
||||||
cur = c;
|
|
||||||
}
|
|
||||||
Ok((out, cur))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn lower_expr(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
e: &ExprV0,
|
|
||||||
) -> Result<(ValueId, BasicBlockId), String> {
|
|
||||||
let mut scope = NoVars;
|
|
||||||
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lower_expr_with_vars(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
e: &ExprV0,
|
|
||||||
vars: &mut HashMap<String, ValueId>,
|
|
||||||
) -> Result<(ValueId, BasicBlockId), String> {
|
|
||||||
let mut scope = MapVars::new(vars);
|
|
||||||
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn lower_args(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
args: &[ExprV0],
|
|
||||||
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
|
||||||
let mut scope = NoVars;
|
|
||||||
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lower_args_with_vars(
|
|
||||||
env: &BridgeEnv,
|
|
||||||
f: &mut MirFunction,
|
|
||||||
cur_bb: BasicBlockId,
|
|
||||||
args: &[ExprV0],
|
|
||||||
vars: &mut HashMap<String, ValueId>,
|
|
||||||
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
|
||||||
let mut scope = MapVars::new(vars);
|
|
||||||
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lower_stmt_with_vars(
|
pub(super) fn lower_stmt_with_vars(
|
||||||
f: &mut MirFunction,
|
f: &mut MirFunction,
|
||||||
@ -516,7 +52,7 @@ pub(super) fn lower_stmt_with_vars(
|
|||||||
) -> Result<BasicBlockId, String> {
|
) -> Result<BasicBlockId, String> {
|
||||||
match s {
|
match s {
|
||||||
StmtV0::Return { expr } => {
|
StmtV0::Return { expr } => {
|
||||||
let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
let (v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
bb.set_terminator(MirInstruction::Return { value: Some(v) });
|
bb.set_terminator(MirInstruction::Return { value: Some(v) });
|
||||||
}
|
}
|
||||||
@ -527,7 +63,7 @@ pub(super) fn lower_stmt_with_vars(
|
|||||||
method,
|
method,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
let (arg_ids, cur) = lower_args_with_vars(env, f, cur_bb, args, vars)?;
|
let (arg_ids, cur) = expr::lower_args_with_vars(env, f, cur_bb, args, vars)?;
|
||||||
if let Some(bb) = f.get_block_mut(cur) {
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
bb.add_instruction(MirInstruction::ExternCall {
|
bb.add_instruction(MirInstruction::ExternCall {
|
||||||
dst: None,
|
dst: None,
|
||||||
@ -540,11 +76,11 @@ pub(super) fn lower_stmt_with_vars(
|
|||||||
Ok(cur)
|
Ok(cur)
|
||||||
}
|
}
|
||||||
StmtV0::Expr { expr } => {
|
StmtV0::Expr { expr } => {
|
||||||
let (_v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
let (_v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
||||||
Ok(cur)
|
Ok(cur)
|
||||||
}
|
}
|
||||||
StmtV0::Local { name, expr } => {
|
StmtV0::Local { name, expr } => {
|
||||||
let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
let (v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
|
||||||
vars.insert(name.clone(), v);
|
vars.insert(name.clone(), v);
|
||||||
Ok(cur)
|
Ok(cur)
|
||||||
}
|
}
|
||||||
|
|||||||
461
src/runner/json_v0_bridge/lowering/expr.rs
Normal file
461
src/runner/json_v0_bridge/lowering/expr.rs
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
use super::merge::new_block;
|
||||||
|
use super::BridgeEnv;
|
||||||
|
use super::ternary;
|
||||||
|
use super::peek;
|
||||||
|
use crate::mir::{
|
||||||
|
BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::super::ast::ExprV0;
|
||||||
|
|
||||||
|
pub(super) trait VarScope {
|
||||||
|
fn resolve(
|
||||||
|
&mut self,
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Option<ValueId>, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct NoVars;
|
||||||
|
impl VarScope for NoVars {
|
||||||
|
fn resolve(
|
||||||
|
&mut self,
|
||||||
|
_env: &BridgeEnv,
|
||||||
|
_f: &mut MirFunction,
|
||||||
|
_cur_bb: BasicBlockId,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Option<ValueId>, String> {
|
||||||
|
Err(format!("undefined variable in this context: {}", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct MapVars<'a> {
|
||||||
|
vars: &'a mut HashMap<String, ValueId>,
|
||||||
|
}
|
||||||
|
impl<'a> MapVars<'a> {
|
||||||
|
fn new(vars: &'a mut HashMap<String, ValueId>) -> Self {
|
||||||
|
Self { vars }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> VarScope for MapVars<'a> {
|
||||||
|
fn resolve(
|
||||||
|
&mut self,
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Option<ValueId>, String> {
|
||||||
|
if let Some(&vid) = self.vars.get(name) {
|
||||||
|
return Ok(Some(vid));
|
||||||
|
}
|
||||||
|
if name == "me" {
|
||||||
|
if env.allow_me_dummy {
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::NewBox {
|
||||||
|
dst,
|
||||||
|
box_type: env.me_class.clone(),
|
||||||
|
args: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.vars.insert(name.to_string(), dst);
|
||||||
|
Ok(Some(dst))
|
||||||
|
} else {
|
||||||
|
Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_throw(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
exception_value: ValueId,
|
||||||
|
) -> (ValueId, BasicBlockId) {
|
||||||
|
if env.throw_enabled {
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.set_terminator(MirInstruction::Throw {
|
||||||
|
exception: exception_value,
|
||||||
|
effects: EffectMask::PANIC,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(exception_value, cur_bb)
|
||||||
|
} else {
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(dst, cur_bb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
e: &ExprV0,
|
||||||
|
vars: &mut S,
|
||||||
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
|
match e {
|
||||||
|
ExprV0::Int { value } => {
|
||||||
|
let ival: i64 = if let Some(n) = value.as_i64() {
|
||||||
|
n
|
||||||
|
} else if let Some(s) = value.as_str() {
|
||||||
|
s.parse().map_err(|_| "invalid int literal")?
|
||||||
|
} else {
|
||||||
|
return Err("invalid int literal".into());
|
||||||
|
};
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Integer(ival),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur_bb))
|
||||||
|
}
|
||||||
|
ExprV0::Str { value } => {
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::String(value.clone()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur_bb))
|
||||||
|
}
|
||||||
|
ExprV0::Bool { value } => {
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Bool(*value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur_bb))
|
||||||
|
}
|
||||||
|
ExprV0::Binary { op, lhs, rhs } => {
|
||||||
|
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
||||||
|
let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
|
||||||
|
let bop = match op.as_str() {
|
||||||
|
"+" => BinaryOp::Add,
|
||||||
|
"-" => BinaryOp::Sub,
|
||||||
|
"*" => BinaryOp::Mul,
|
||||||
|
"/" => BinaryOp::Div,
|
||||||
|
_ => return Err("unsupported op".into()),
|
||||||
|
};
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
||||||
|
bb.add_instruction(MirInstruction::BinOp {
|
||||||
|
dst,
|
||||||
|
op: bop,
|
||||||
|
lhs: l,
|
||||||
|
rhs: r,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur_after_r))
|
||||||
|
}
|
||||||
|
ExprV0::Extern {
|
||||||
|
iface,
|
||||||
|
method,
|
||||||
|
args,
|
||||||
|
} => {
|
||||||
|
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur2) {
|
||||||
|
bb.add_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: Some(dst),
|
||||||
|
iface_name: iface.clone(),
|
||||||
|
method_name: method.clone(),
|
||||||
|
args: arg_ids,
|
||||||
|
effects: EffectMask::IO,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur2))
|
||||||
|
}
|
||||||
|
ExprV0::Compare { op, lhs, rhs } => {
|
||||||
|
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
||||||
|
let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
|
||||||
|
let cop = match op.as_str() {
|
||||||
|
"==" => crate::mir::CompareOp::Eq,
|
||||||
|
"!=" => crate::mir::CompareOp::Ne,
|
||||||
|
"<" => crate::mir::CompareOp::Lt,
|
||||||
|
"<=" => crate::mir::CompareOp::Le,
|
||||||
|
">" => crate::mir::CompareOp::Gt,
|
||||||
|
">=" => crate::mir::CompareOp::Ge,
|
||||||
|
_ => return Err("unsupported compare op".into()),
|
||||||
|
};
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
||||||
|
bb.add_instruction(MirInstruction::Compare {
|
||||||
|
dst,
|
||||||
|
op: cop,
|
||||||
|
lhs: l,
|
||||||
|
rhs: r,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur_after_r))
|
||||||
|
}
|
||||||
|
ExprV0::Logical { op, lhs, rhs } => {
|
||||||
|
let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
|
||||||
|
let rhs_bb = new_block(f);
|
||||||
|
let fall_bb = new_block(f);
|
||||||
|
let merge_bb = new_block(f);
|
||||||
|
let is_and = matches!(op.as_str(), "&&" | "and");
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_after_l) {
|
||||||
|
if is_and {
|
||||||
|
bb.set_terminator(MirInstruction::Branch {
|
||||||
|
condition: l,
|
||||||
|
then_bb: rhs_bb,
|
||||||
|
else_bb: fall_bb,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bb.set_terminator(MirInstruction::Branch {
|
||||||
|
condition: l,
|
||||||
|
then_bb: fall_bb,
|
||||||
|
else_bb: rhs_bb,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::jit::events::emit_lower(
|
||||||
|
serde_json::json!({ "id":"shortcircuit","op": if is_and {"and"} else {"or"},"rhs_bb":rhs_bb.0,"fall_bb":fall_bb.0,"merge_bb":merge_bb.0 }),
|
||||||
|
"shortcircuit",
|
||||||
|
"<json_v0>",
|
||||||
|
);
|
||||||
|
let cdst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(fall_bb) {
|
||||||
|
let cval = if is_and {
|
||||||
|
ConstValue::Bool(false)
|
||||||
|
} else {
|
||||||
|
ConstValue::Bool(true)
|
||||||
|
};
|
||||||
|
bb.add_instruction(MirInstruction::Const { dst: cdst, value: cval });
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?;
|
||||||
|
if let Some(bb) = f.get_block_mut(rhs_end) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let out = f.next_value_id();
|
||||||
|
if env.mir_no_phi {
|
||||||
|
if let Some(bb) = f.get_block_mut(fall_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::Copy { dst: out, src: cdst });
|
||||||
|
}
|
||||||
|
if let Some(bb) = f.get_block_mut(rhs_end) {
|
||||||
|
bb.add_instruction(MirInstruction::Copy { dst: out, src: rval });
|
||||||
|
}
|
||||||
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
||||||
|
let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
|
||||||
|
if rhs_end != fall_bb {
|
||||||
|
inputs.push((rhs_end, rval));
|
||||||
|
} else {
|
||||||
|
inputs.push((fall_bb, rval));
|
||||||
|
}
|
||||||
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||||
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
|
||||||
|
}
|
||||||
|
Ok((out, merge_bb))
|
||||||
|
}
|
||||||
|
ExprV0::Call { name, args } => {
|
||||||
|
if name == "array.of" {
|
||||||
|
let arr = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::NewBox {
|
||||||
|
dst: arr,
|
||||||
|
box_type: "ArrayBox".into(),
|
||||||
|
args: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut cur = cur_bb;
|
||||||
|
for e in args {
|
||||||
|
let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?;
|
||||||
|
cur = c;
|
||||||
|
let tmp = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.add_instruction(MirInstruction::BoxCall {
|
||||||
|
dst: Some(tmp),
|
||||||
|
box_val: arr,
|
||||||
|
method: "push".into(),
|
||||||
|
method_id: None,
|
||||||
|
args: vec![v],
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok((arr, cur));
|
||||||
|
}
|
||||||
|
if name == "map.of" {
|
||||||
|
let mapv = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||||
|
bb.add_instruction(MirInstruction::NewBox {
|
||||||
|
dst: mapv,
|
||||||
|
box_type: "MapBox".into(),
|
||||||
|
args: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut cur = cur_bb;
|
||||||
|
let mut it = args.iter();
|
||||||
|
while let Some(k) = it.next() {
|
||||||
|
if let Some(v) = it.next() {
|
||||||
|
let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?;
|
||||||
|
cur = cur2;
|
||||||
|
let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?;
|
||||||
|
cur = cur3;
|
||||||
|
let tmp = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.add_instruction(MirInstruction::BoxCall {
|
||||||
|
dst: Some(tmp),
|
||||||
|
box_val: mapv,
|
||||||
|
method: "set".into(),
|
||||||
|
method_id: None,
|
||||||
|
args: vec![kv, vv],
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok((mapv, cur));
|
||||||
|
}
|
||||||
|
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||||
|
let fun_val = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.add_instruction(MirInstruction::Const { dst: fun_val, value: ConstValue::String(name.clone()) });
|
||||||
|
}
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.add_instruction(MirInstruction::Call {
|
||||||
|
dst: Some(dst),
|
||||||
|
func: fun_val,
|
||||||
|
args: arg_ids,
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur))
|
||||||
|
}
|
||||||
|
ExprV0::Method { recv, method, args } => {
|
||||||
|
let recv_is_console_new = matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
||||||
|
if recv_is_console_new && (method == "println" || method == "print" || method == "log") {
|
||||||
|
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur2) {
|
||||||
|
bb.add_instruction(MirInstruction::ExternCall {
|
||||||
|
dst: Some(dst),
|
||||||
|
iface_name: "env.console".into(),
|
||||||
|
method_name: "log".into(),
|
||||||
|
args: arg_ids,
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Ok((dst, cur2));
|
||||||
|
}
|
||||||
|
let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?;
|
||||||
|
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?;
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur2) {
|
||||||
|
bb.add_instruction(MirInstruction::BoxCall {
|
||||||
|
dst: Some(dst),
|
||||||
|
box_val: recv_v,
|
||||||
|
method: method.clone(),
|
||||||
|
method_id: None,
|
||||||
|
args: arg_ids,
|
||||||
|
effects: EffectMask::READ,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok((dst, cur2))
|
||||||
|
}
|
||||||
|
ExprV0::New { class, args } => {
|
||||||
|
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||||
|
let dst = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.add_instruction(MirInstruction::NewBox { dst, box_type: class.clone(), args: arg_ids });
|
||||||
|
}
|
||||||
|
Ok((dst, cur))
|
||||||
|
}
|
||||||
|
ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? {
|
||||||
|
Some(v) => Ok((v, cur_bb)),
|
||||||
|
None => Err(format!("undefined variable: {}", name)),
|
||||||
|
},
|
||||||
|
ExprV0::Throw { expr } => {
|
||||||
|
let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?;
|
||||||
|
Ok(lower_throw(env, f, cur, exc))
|
||||||
|
}
|
||||||
|
ExprV0::Ternary { cond, then, r#else } =>
|
||||||
|
ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars),
|
||||||
|
ExprV0::Peek { scrutinee, arms, r#else } =>
|
||||||
|
peek::lower_peek_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_args_with_scope<S: VarScope>(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
args: &[ExprV0],
|
||||||
|
scope: &mut S,
|
||||||
|
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
||||||
|
let mut out = Vec::with_capacity(args.len());
|
||||||
|
let mut cur = cur_bb;
|
||||||
|
for a in args {
|
||||||
|
let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?;
|
||||||
|
out.push(v);
|
||||||
|
cur = c;
|
||||||
|
}
|
||||||
|
Ok((out, cur))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn lower_expr(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
e: &ExprV0,
|
||||||
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
|
let mut scope = NoVars;
|
||||||
|
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_expr_with_vars(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
e: &ExprV0,
|
||||||
|
vars: &mut HashMap<String, ValueId>,
|
||||||
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
|
let mut scope = MapVars::new(vars);
|
||||||
|
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn lower_args(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
args: &[ExprV0],
|
||||||
|
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
||||||
|
let mut scope = NoVars;
|
||||||
|
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_args_with_vars(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
args: &[ExprV0],
|
||||||
|
vars: &mut HashMap<String, ValueId>,
|
||||||
|
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
|
||||||
|
let mut scope = MapVars::new(vars);
|
||||||
|
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
|
||||||
|
}
|
||||||
@ -1,7 +1,5 @@
|
|||||||
use super::{
|
use super::{lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, LoopContext};
|
||||||
lower_expr_with_vars, lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv,
|
use super::expr::lower_expr_with_vars;
|
||||||
LoopContext,
|
|
||||||
};
|
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use super::super::ast::StmtV0;
|
use super::super::ast::StmtV0;
|
||||||
@ -17,7 +15,7 @@ pub(super) fn lower_if_stmt(
|
|||||||
loop_stack: &mut Vec<LoopContext>,
|
loop_stack: &mut Vec<LoopContext>,
|
||||||
env: &BridgeEnv,
|
env: &BridgeEnv,
|
||||||
) -> Result<BasicBlockId, String> {
|
) -> Result<BasicBlockId, String> {
|
||||||
let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?;
|
let (cval, cur) = super::expr::lower_expr_with_vars(env, f, cur_bb, cond, vars)?;
|
||||||
let then_bb = new_block(f);
|
let then_bb = new_block(f);
|
||||||
let else_bb = new_block(f);
|
let else_bb = new_block(f);
|
||||||
let merge_bb = new_block(f);
|
let merge_bb = new_block(f);
|
||||||
@ -64,4 +62,3 @@ pub(super) fn lower_if_stmt(
|
|||||||
);
|
);
|
||||||
Ok(merge_bb)
|
Ok(merge_bb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use super::{
|
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||||
lower_expr_with_vars, lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext,
|
use super::expr::lower_expr_with_vars;
|
||||||
};
|
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use super::super::ast::StmtV0;
|
use super::super::ast::StmtV0;
|
||||||
@ -46,7 +45,7 @@ pub(super) fn lower_loop_stmt(
|
|||||||
for (name, &phi) in &phi_map {
|
for (name, &phi) in &phi_map {
|
||||||
vars.insert(name.clone(), phi);
|
vars.insert(name.clone(), phi);
|
||||||
}
|
}
|
||||||
let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?;
|
let (cval, _cend) = super::expr::lower_expr_with_vars(env, f, cond_bb, cond, vars)?;
|
||||||
if let Some(bb) = f.get_block_mut(cond_bb) {
|
if let Some(bb) = f.get_block_mut(cond_bb) {
|
||||||
bb.set_terminator(MirInstruction::Branch {
|
bb.set_terminator(MirInstruction::Branch {
|
||||||
condition: cval,
|
condition: cval,
|
||||||
@ -102,4 +101,3 @@ pub(super) fn lower_loop_stmt(
|
|||||||
}
|
}
|
||||||
Ok(exit_bb)
|
Ok(exit_bb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
src/runner/json_v0_bridge/lowering/peek.rs
Normal file
84
src/runner/json_v0_bridge/lowering/peek.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! Peek/expr-block lowering for JSON v0 bridge.
|
||||||
|
|
||||||
|
use super::merge::new_block;
|
||||||
|
use super::BridgeEnv;
|
||||||
|
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
|
||||||
|
use super::super::ast::{ExprV0, PeekArmV0};
|
||||||
|
use super::expr::{lower_expr_with_scope, VarScope};
|
||||||
|
|
||||||
|
pub(super) fn lower_peek_expr_with_scope<S: VarScope>(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
scrutinee: &ExprV0,
|
||||||
|
arms: &[PeekArmV0],
|
||||||
|
else_expr: &ExprV0,
|
||||||
|
vars: &mut S,
|
||||||
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
|
// Evaluate scrutinee
|
||||||
|
let (scr_val, start_bb) = lower_expr_with_scope(env, f, cur_bb, scrutinee, vars)?;
|
||||||
|
|
||||||
|
// Set up blocks
|
||||||
|
let dispatch_bb = new_block(f);
|
||||||
|
if let Some(bb) = f.get_block_mut(start_bb) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: dispatch_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let else_bb = new_block(f);
|
||||||
|
let merge_bb = new_block(f);
|
||||||
|
|
||||||
|
// Chain dispatch over arms
|
||||||
|
let mut cur_dispatch = dispatch_bb;
|
||||||
|
let mut phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||||
|
for (i, arm) in arms.iter().enumerate() {
|
||||||
|
let then_bb = new_block(f);
|
||||||
|
let next_dispatch = if i + 1 < arms.len() { Some(new_block(f)) } else { None };
|
||||||
|
let fall_bb = next_dispatch.unwrap_or(else_bb);
|
||||||
|
|
||||||
|
// Pre-allocate ids to avoid double borrow
|
||||||
|
let ldst = f.next_value_id();
|
||||||
|
let cond = f.next_value_id();
|
||||||
|
if let Some(bb) = f.get_block_mut(cur_dispatch) {
|
||||||
|
// compare scr_val == label
|
||||||
|
bb.add_instruction(MirInstruction::Const { dst: ldst, value: ConstValue::String(arm.label.clone()) });
|
||||||
|
bb.add_instruction(MirInstruction::Compare { dst: cond, op: CompareOp::Eq, lhs: scr_val, rhs: ldst });
|
||||||
|
bb.set_terminator(MirInstruction::Branch { condition: cond, then_bb, else_bb: fall_bb });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then arm body
|
||||||
|
let (tval, tend) = lower_expr_with_scope(env, f, then_bb, &arm.expr, vars)?;
|
||||||
|
if let Some(bb) = f.get_block_mut(tend) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
phi_inputs.push((tend, tval));
|
||||||
|
|
||||||
|
cur_dispatch = fall_bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else body
|
||||||
|
let (eval, eend) = lower_expr_with_scope(env, f, else_bb, else_expr, vars)?;
|
||||||
|
if let Some(bb) = f.get_block_mut(eend) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
phi_inputs.push((eend, eval));
|
||||||
|
|
||||||
|
// Merge result
|
||||||
|
let out = f.next_value_id();
|
||||||
|
if env.mir_no_phi {
|
||||||
|
for (pred, val) in phi_inputs {
|
||||||
|
if let Some(bb) = f.get_block_mut(pred) {
|
||||||
|
bb.add_instruction(MirInstruction::Copy { dst: out, src: val });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
||||||
|
let mut inputs = phi_inputs;
|
||||||
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||||
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
|
||||||
|
}
|
||||||
|
Ok((out, merge_bb))
|
||||||
|
}
|
||||||
60
src/runner/json_v0_bridge/lowering/ternary.rs
Normal file
60
src/runner/json_v0_bridge/lowering/ternary.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//! Ternary lowering (skeleton)
|
||||||
|
//!
|
||||||
|
//! NOTE: This module is introduced as part of the helper split.
|
||||||
|
//! It is not wired yet and should not alter behavior.
|
||||||
|
|
||||||
|
use super::merge::new_block;
|
||||||
|
use super::BridgeEnv;
|
||||||
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
|
use super::super::ast::ExprV0;
|
||||||
|
|
||||||
|
use super::expr::{lower_expr_with_scope, VarScope};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(super) fn lower_ternary_expr_with_scope<S: VarScope>(
|
||||||
|
env: &BridgeEnv,
|
||||||
|
f: &mut MirFunction,
|
||||||
|
cur_bb: BasicBlockId,
|
||||||
|
cond: &ExprV0,
|
||||||
|
then_e: &ExprV0,
|
||||||
|
else_e: &ExprV0,
|
||||||
|
vars: &mut S,
|
||||||
|
) -> Result<(ValueId, BasicBlockId), String> {
|
||||||
|
let (cval, cur) = lower_expr_with_scope(env, f, cur_bb, cond, vars)?;
|
||||||
|
let then_bb = new_block(f);
|
||||||
|
let else_bb = new_block(f);
|
||||||
|
let merge_bb = new_block(f);
|
||||||
|
if let Some(bb) = f.get_block_mut(cur) {
|
||||||
|
bb.set_terminator(MirInstruction::Branch {
|
||||||
|
condition: cval,
|
||||||
|
then_bb,
|
||||||
|
else_bb,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let (tval, tend) = lower_expr_with_scope(env, f, then_bb, then_e, vars)?;
|
||||||
|
if let Some(bb) = f.get_block_mut(tend) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (eval, eend) = lower_expr_with_scope(env, f, else_bb, else_e, vars)?;
|
||||||
|
if let Some(bb) = f.get_block_mut(eend) {
|
||||||
|
if !bb.is_terminated() {
|
||||||
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let out = f.next_value_id();
|
||||||
|
if env.mir_no_phi {
|
||||||
|
if let Some(bb) = f.get_block_mut(tend) {
|
||||||
|
bb.add_instruction(MirInstruction::Copy { dst: out, src: tval });
|
||||||
|
}
|
||||||
|
if let Some(bb) = f.get_block_mut(eend) {
|
||||||
|
bb.add_instruction(MirInstruction::Copy { dst: out, src: eval });
|
||||||
|
}
|
||||||
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
||||||
|
let mut inputs = vec![(tend, tval), (eend, eval)];
|
||||||
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||||
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
|
||||||
|
}
|
||||||
|
Ok((out, merge_bb))
|
||||||
|
}
|
||||||
@ -90,7 +90,7 @@ pub(super) fn lower_try_stmt(
|
|||||||
let catch_branch_vars = catch_vars.clone();
|
let catch_branch_vars = catch_vars.clone();
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
let mut branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)];
|
let branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)];
|
||||||
if let Some(finally_block) = finally_bb {
|
if let Some(finally_block) = finally_bb {
|
||||||
let names: HashSet<String> = {
|
let names: HashSet<String> = {
|
||||||
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
|
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
|
||||||
@ -187,4 +187,3 @@ pub(super) fn lower_try_stmt(
|
|||||||
Ok(exit_bb)
|
Ok(exit_bb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use nyash_rust::{interpreter::NyashInterpreter, parser::NyashParser};
|
use nyash_rust::parser::NyashParser;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
|||||||
@ -141,5 +141,26 @@ else
|
|||||||
fail "Ternary basic" "exit=$CODE"
|
fail "Ternary basic" "exit=$CODE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# K) peek expression → 1
|
||||||
|
cat > "$TMP/selfhost_peek_basic.nyash" <<'NY'
|
||||||
|
local d = "dog"
|
||||||
|
local v = peek d {
|
||||||
|
"cat" => { 0 }
|
||||||
|
"dog" => { 1 }
|
||||||
|
else => { 0 }
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
NY
|
||||||
|
set +e
|
||||||
|
NYASH_USE_NY_COMPILER=1 NYASH_NY_COMPILER_EMIT_ONLY=0 NYASH_VM_USE_PY=${NYASH_VM_USE_PY:-1} \
|
||||||
|
"$BIN" --backend vm "$TMP/selfhost_peek_basic.nyash" >/dev/null 2>&1
|
||||||
|
CODE=$?
|
||||||
|
set -e
|
||||||
|
if [[ "$CODE" -eq 1 ]]; then
|
||||||
|
pass "Peek basic"
|
||||||
|
else
|
||||||
|
fail "Peek basic" "exit=$CODE"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "All selfhost Stage-2 smokes PASS" >&2
|
echo "All selfhost Stage-2 smokes PASS" >&2
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Reference in New Issue
Block a user