Phase 21.6 solidification: chain green (return/binop/loop/call); add Phase 21.7 normalization plan (methodize static boxes). Update CURRENT_TASK.md and docs.
This commit is contained in:
@ -802,6 +802,30 @@ impl MirInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: user-defined function dispatch for Global calls
|
||||
// If none of the above extern/provider/global bridges matched,
|
||||
// try to resolve and execute a user function present in this module.
|
||||
{
|
||||
// Use unique-tail resolver against snapshot of self.functions
|
||||
let fname = call_resolution::resolve_function_name(
|
||||
func_name,
|
||||
args.len(),
|
||||
&self.functions,
|
||||
self.cur_fn.as_deref(),
|
||||
);
|
||||
if let Some(fname) = fname {
|
||||
if let Some(func) = self.functions.get(&fname).cloned() {
|
||||
// Load arguments and execute
|
||||
let mut argv: Vec<VMValue> = Vec::new();
|
||||
for a in args { argv.push(self.reg_load(*a)?); }
|
||||
if std::env::var("NYASH_VM_CALL_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[vm] global-call resolved '{}' -> '{}'", func_name, fname);
|
||||
}
|
||||
return self.exec_function_inner(&func, Some(&argv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_extern_function(
|
||||
&mut self,
|
||||
extern_name: &str,
|
||||
|
||||
@ -196,8 +196,15 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
|
||||
try:
|
||||
dst = inst.get("dst")
|
||||
if isinstance(dst, int):
|
||||
if dst in builder.vmap:
|
||||
# Prefer current vmap context (_current_vmap) updates; fallback to global vmap
|
||||
_gval = None
|
||||
try:
|
||||
_gval = vmap_cur.get(dst)
|
||||
except Exception:
|
||||
_gval = None
|
||||
if _gval is None and dst in builder.vmap:
|
||||
_gval = builder.vmap[dst]
|
||||
if _gval is not None:
|
||||
try:
|
||||
if hasattr(_gval, 'add_incoming'):
|
||||
bb_of = getattr(getattr(_gval, 'basic_block', None), 'name', None)
|
||||
|
||||
@ -30,6 +30,13 @@ def lower_function(builder, func_data: Dict[str, Any]):
|
||||
# Default: i64(i64, ...) signature; derive arity from '/N' suffix when params missing
|
||||
m = re.search(r"/(\d+)$", name)
|
||||
arity = int(m.group(1)) if m else len(params)
|
||||
# Dev fallback: when params are missing for global (Box.method) functions,
|
||||
# use observed call-site arity if available (scanned in builder.build_from_mir)
|
||||
if arity == 0 and '.' in name:
|
||||
try:
|
||||
arity = int(builder.call_arities.get(name, 0))
|
||||
except Exception:
|
||||
pass
|
||||
param_types = [builder.i64] * arity
|
||||
func_ty = ir.FunctionType(builder.i64, param_types)
|
||||
|
||||
@ -67,11 +74,38 @@ def lower_function(builder, func_data: Dict[str, Any]):
|
||||
if func is None:
|
||||
func = ir.Function(builder.module, func_ty, name=name)
|
||||
|
||||
# Map parameters to vmap (value_id: 0..arity-1)
|
||||
# Map parameters to vmap. Prefer mapping by referenced value-ids that have no
|
||||
# local definition (common in v0 JSON where params appear as lhs/rhs ids).
|
||||
try:
|
||||
arity = len(func.args)
|
||||
# Collect defined and used ids
|
||||
defs = set()
|
||||
uses = set()
|
||||
for bb in (blocks or []):
|
||||
for ins in (bb.get('instructions') or []):
|
||||
try:
|
||||
dstv = ins.get('dst')
|
||||
if isinstance(dstv, int):
|
||||
defs.add(int(dstv))
|
||||
except Exception:
|
||||
pass
|
||||
for k in ('lhs','rhs','value','cond','box_val'):
|
||||
try:
|
||||
v = ins.get(k)
|
||||
if isinstance(v, int):
|
||||
uses.add(int(v))
|
||||
except Exception:
|
||||
pass
|
||||
cand = [vid for vid in uses if vid not in defs]
|
||||
cand.sort()
|
||||
mapped = 0
|
||||
for i in range(min(arity, len(cand))):
|
||||
builder.vmap[int(cand[i])] = func.args[i]
|
||||
mapped += 1
|
||||
# Fallback: also map positional 0..arity-1 to args if not already mapped
|
||||
for i in range(arity):
|
||||
builder.vmap[i] = func.args[i]
|
||||
if i not in builder.vmap:
|
||||
builder.vmap[i] = func.args[i]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@ -133,6 +133,46 @@ class NyashLLVMBuilder:
|
||||
# Parse MIR
|
||||
reader = MIRReader(mir_json)
|
||||
functions = reader.get_functions()
|
||||
|
||||
# Pre-scan call sites to estimate arity for global functions when params are missing
|
||||
def _scan_call_arities(funcs: List[Dict[str, Any]]):
|
||||
ar: Dict[str, int] = {}
|
||||
for f in funcs or []:
|
||||
# Build map: const dst -> string name (per-function scope)
|
||||
const_names: Dict[int, str] = {}
|
||||
for bb in (f.get('blocks') or []):
|
||||
for ins in (bb.get('instructions') or []):
|
||||
try:
|
||||
op = ins.get('op')
|
||||
if op == 'const':
|
||||
dst = ins.get('dst')
|
||||
val = ins.get('value') or {}
|
||||
name = None
|
||||
if isinstance(val, dict):
|
||||
v = val.get('value')
|
||||
t = val.get('type')
|
||||
if isinstance(v, str) and (
|
||||
t == 'string' or (isinstance(t, dict) and t.get('box_type') == 'StringBox')
|
||||
):
|
||||
name = v
|
||||
if isinstance(dst, int) and isinstance(name, str):
|
||||
const_names[int(dst)] = name
|
||||
elif op == 'call':
|
||||
func_id = ins.get('func')
|
||||
if isinstance(func_id, int) and func_id in const_names:
|
||||
nm = const_names[func_id]
|
||||
argc = len(ins.get('args') or [])
|
||||
prev = ar.get(nm, 0)
|
||||
if argc > prev:
|
||||
ar[nm] = argc
|
||||
except Exception:
|
||||
continue
|
||||
return ar
|
||||
|
||||
try:
|
||||
self.call_arities = _scan_call_arities(functions)
|
||||
except Exception:
|
||||
self.call_arities = {}
|
||||
|
||||
if not functions:
|
||||
# No functions - create dummy ny_main
|
||||
@ -149,6 +189,12 @@ class NyashLLVMBuilder:
|
||||
params_list = func_data.get("params", []) or []
|
||||
if "." in name:
|
||||
arity = len(params_list)
|
||||
# Dev fallback: when params missing for Box.method, use call-site arity
|
||||
if arity == 0:
|
||||
try:
|
||||
arity = int(self.call_arities.get(name, 0))
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
arity = int(m.group(1)) if m else len(params_list)
|
||||
if name == "ny_main":
|
||||
|
||||
@ -1,10 +1,21 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub(super) struct ProgramV0 {
|
||||
pub(super) version: i32,
|
||||
pub(super) kind: String,
|
||||
pub(super) body: Vec<StmtV0>,
|
||||
#[serde(default)]
|
||||
pub(super) defs: Vec<FuncDefV0>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub(super) struct FuncDefV0 {
|
||||
pub(super) name: String,
|
||||
pub(super) params: Vec<String>,
|
||||
pub(super) body: ProgramV0,
|
||||
#[serde(rename = "box")]
|
||||
pub(super) box_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
|
||||
@ -185,6 +185,7 @@ pub(super) fn parse_source_v0_to_json(input: &str) -> Result<String, String> {
|
||||
version: 0,
|
||||
kind: "Program".into(),
|
||||
body: vec![StmtV0::Return { expr }],
|
||||
defs: vec![],
|
||||
};
|
||||
serde_json::to_string(&prog).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@ -293,6 +293,103 @@ pub(super) fn lower_program(prog: ProgramV0) -> Result<MirModule, String> {
|
||||
f.signature.return_type = MirType::Unknown;
|
||||
// フェーズM.2: PHI後処理削除 - MirBuilder/LoopBuilderでPHI統一済み
|
||||
module.add_function(f);
|
||||
|
||||
// Phase 21.6: Process function definitions (defs)
|
||||
// Toggle: HAKO_STAGEB_FUNC_SCAN=1 + HAKO_MIR_BUILDER_FUNCS=1
|
||||
// Minimal support: Return(Int|Binary(+|-|*|/, Int|Var, Int|Var))
|
||||
let mut func_map: HashMap<String, String> = HashMap::new();
|
||||
if !prog.defs.is_empty() {
|
||||
for func_def in prog.defs {
|
||||
// Create function signature: Main.<name>
|
||||
let func_name = format!("{}.{}", func_def.box_name, func_def.name);
|
||||
|
||||
// Register function in map for Call resolution
|
||||
func_map.insert(func_def.name.clone(), func_name.clone());
|
||||
|
||||
let param_ids: Vec<ValueId> = (0..func_def.params.len())
|
||||
.map(|i| ValueId::new(i as u32 + 1))
|
||||
.collect();
|
||||
let param_types: Vec<MirType> = (0..func_def.params.len())
|
||||
.map(|_| MirType::Unknown)
|
||||
.collect();
|
||||
let sig = FunctionSignature {
|
||||
name: func_name,
|
||||
params: param_types,
|
||||
return_type: MirType::Integer,
|
||||
effects: EffectMask::PURE,
|
||||
};
|
||||
let entry = BasicBlockId::new(0);
|
||||
let mut func = MirFunction::new(sig, entry);
|
||||
|
||||
// Map params to value IDs
|
||||
let mut func_var_map: HashMap<String, ValueId> = HashMap::new();
|
||||
for (i, param_name) in func_def.params.iter().enumerate() {
|
||||
func_var_map.insert(param_name.clone(), param_ids[i]);
|
||||
}
|
||||
|
||||
// Lower function body
|
||||
let mut loop_stack: Vec<LoopContext> = Vec::new();
|
||||
let start_bb = func.entry_block;
|
||||
let _end_bb = lower_stmt_list_with_vars(
|
||||
&mut func,
|
||||
start_bb,
|
||||
&func_def.body.body,
|
||||
&mut func_var_map,
|
||||
&mut loop_stack,
|
||||
&env,
|
||||
)?;
|
||||
|
||||
func.signature.return_type = MirType::Unknown;
|
||||
module.add_function(func);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 21.6: Call resolution post-processing
|
||||
// Toggle: HAKO_MIR_BUILDER_CALL_RESOLVE=1
|
||||
// Resolve Call instructions to use qualified function names (e.g., "add" -> "Main.add")
|
||||
if std::env::var("HAKO_MIR_BUILDER_CALL_RESOLVE").ok().as_deref() == Some("1") {
|
||||
if !func_map.is_empty() {
|
||||
for (_func_idx, func) in module.functions.iter_mut() {
|
||||
for (_block_id, block) in func.blocks.iter_mut() {
|
||||
let mut const_replacements: Vec<(ValueId, String)> = Vec::new();
|
||||
|
||||
// Find Call instructions and their associated Const values
|
||||
for inst in &block.instructions {
|
||||
if let MirInstruction::Call { func: func_reg, .. } = inst {
|
||||
// Look for the Const instruction that defines func_reg
|
||||
for const_inst in &block.instructions {
|
||||
if let MirInstruction::Const { dst, value } = const_inst {
|
||||
if dst == func_reg {
|
||||
if let ConstValue::String(name) = value {
|
||||
// Try to resolve the name
|
||||
if let Some(resolved) = func_map.get(name) {
|
||||
const_replacements.push((*dst, resolved.clone()));
|
||||
if std::env::var("HAKO_MIR_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
eprintln!("[mirbuilder/call:resolve] {} => {}", name, resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply replacements
|
||||
for (dst, new_name) in const_replacements {
|
||||
for inst in &mut block.instructions {
|
||||
if let MirInstruction::Const { dst: d, value } = inst {
|
||||
if d == &dst {
|
||||
*value = ConstValue::String(new_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user