@@ -176,7 +176,7 @@ static box LocalSSA {
176176 cur = obj_end
177177 continue
178178 }
179- // Insert copy at the start of the block
179+ // Insert copy after PHI cluster at block head
180180 // Trace: capture block id of this branch's block
181181 local bid = 0
182182 // Find nearest '"id":<n>' before this block's instructions
@@ -187,9 +187,22 @@ static box LocalSSA {
187187 if bid_csv == "" { bid_csv = "" + bid } else { bid_csv = bid_csv + "," + bid }
188188 }
189189 }
190+ // Compute insertion point just after contiguous PHI nodes
191+ local insert_at = insts_start
192+ {
193+ local scan = insts_start
194+ loop(true) {
195+ local pphi = me._index_of_from(out, "\"op\":\"phi\"", scan)
196+ if pphi < 0 || pphi >= obj_start { break }
197+ local sphi = me._seek_obj_start(out, pphi)
198+ local ephi = me._seek_obj_end(out, sphi)
199+ insert_at = ephi
200+ scan = ephi
201+ }
202+ }
190203 local new_id = me._max_dst_id(out) + 1
191204 local copy_inst = "{\\\"op\\\":\\\"copy\\\",\\\"src\\\":" + digits + ",\\\"dst\\\":" + new_id + "},"
192- out = out.substring(0, insts_start ) + copy_inst + out.substring(insts_start , out.length())
205+ out = out.substring(0, insert_at ) + copy_inst + out.substring(insert_at , out.length())
193206 t_copies = t_copies + 1
194207 // Rewrite branch cond to new_id within obj segment (adjust obj positions by insertion)
195208 local delta = copy_inst.length()
@@ -258,6 +271,249 @@ static box LocalSSA {
258271 return json_text
259272 }
260273
274+ // Experimental: call-site operand materialization (minimal)
275+ // Insert copy instructions after the PHI cluster of a block when call/method/new operands
276+ // (recv/args) are not defined earlier in the same block. This keeps operands locally materialized
277+ // without changing semantics. JSON is manipulated textually to avoid adding heavy dependencies.
278+ ensure_calls(json_text) {
279+ if json_text == null { return json_text }
280+ // Only operate on MIR(JSON) text; Stage‑1 remains unchanged
281+ if me._is_mir_json(json_text) == 0 { return json_text }
282+ local out = json_text
283+ local cur = 0
284+ loop(true) {
285+ // Find next call-like instruction (v0 and v1 tolerant)
286+ local p1 = me._index_of_from(out, "\"op\":\"call\"", cur)
287+ local p2 = me._index_of_from(out, "\"op\":\"boxcall\"", cur)
288+ local p3 = me._index_of_from(out, "\"op\":\"newbox\"", cur)
289+ local p4 = me._index_of_from(out, "\"op\":\"mir_call\"", cur)
290+ // choose earliest >=0
291+ local op_pos = -1
292+ local kind = 0 // 1=call,2=boxcall,3=newbox,4=mir_call
293+ if p1 >= 0 { op_pos = p1 kind = 1 }
294+ if p2 >= 0 && (op_pos < 0 || p2 < op_pos) { op_pos = p2 kind = 2 }
295+ if p3 >= 0 && (op_pos < 0 || p3 < op_pos) { op_pos = p3 kind = 3 }
296+ if p4 >= 0 && (op_pos < 0 || p4 < op_pos) { op_pos = p4 kind = 4 }
297+ if op_pos < 0 { break }
298+
299+ // Determine instruction object and enclosing block bounds
300+ local obj_start = me._seek_obj_start(out, op_pos)
301+ local obj_end = me._seek_obj_end(out, obj_start)
302+ local insts_start = me._block_insts_start(out, obj_start)
303+ if insts_start < 0 { cur = obj_end continue }
304+
305+ // Compute insertion point: after the contiguous PHI cluster at the block head
306+ local insert_at = insts_start
307+ local scan = insts_start
308+ loop(true) {
309+ local pphi = me._index_of_from(out, "\"op\":\"phi\"", scan)
310+ if pphi < 0 || pphi >= obj_start { break }
311+ local sphi = me._seek_obj_start(out, pphi)
312+ local ephi = me._seek_obj_end(out, sphi)
313+ insert_at = ephi
314+ scan = ephi
315+ }
316+
317+ // Inline helpers are expanded where needed(Nyash: avoid nested func capture for portability)
318+
319+ // V0: args array inside the instruction object
320+ if kind == 1 || kind == 2 || kind == 3 {
321+ // Receiver for boxcall
322+ if kind == 2 {
323+ local bk = "\"box\":"
324+ local bp = me._index_of_from(out, bk, obj_start)
325+ if bp >= 0 && bp < obj_end {
326+ local digits = me._read_digits(out, bp + bk.length())
327+ // Check if dst:<digits> exists before this instruction in the block
328+ local has_def = 0
329+ if digits != "" {
330+ local def_pat = "\"dst\":" + digits
331+ local s = insts_start
332+ loop(true) {
333+ local p = me._index_of_from(out, def_pat, s)
334+ if p < 0 || p >= obj_start { break }
335+ has_def = 1
336+ break
337+ }
338+ }
339+ if digits != "" && has_def == 0 {
340+ // Insert copy and rewrite box id
341+ local new_id = me._max_dst_id(out) + 1
342+ local copy_inst = "{\\\"op\\\":\\\"copy\\\",\\\"src\\\":" + digits + ",\\\"dst\\\":" + new_id + "},"
343+ out = out.substring(0, insert_at) + copy_inst + out.substring(insert_at, out.length())
344+ local delta = copy_inst.length()
345+ if insert_at <= obj_start { obj_start = obj_start + delta obj_end = obj_end + delta }
346+ // rewrite digits at bp
347+ local head = out.substring(0, bp + bk.length())
348+ local di = bp + bk.length()
349+ loop(true) {
350+ local ch = out.substring(di, di+1)
351+ if ch >= "0" && ch <= "9" { di = di + 1 } else { break }
352+ }
353+ local tail = out.substring(di, out.length())
354+ out = head + new_id + tail
355+ }
356+ }
357+ }
358+ // Args list
359+ local ak = me._index_of_from(out, "\"args\":[", obj_start)
360+ if ak >= 0 && ak < obj_end {
361+ // find end of args list (first closing ']' after ak)
362+ local i = ak + 8
363+ local rb = i
364+ loop(true) {
365+ local ch = out.substring(i, i+1)
366+ if ch == "]" || ch == "" { rb = i break }
367+ i = i + 1
368+ }
369+ // iterate over digits inside args
370+ local pos = ak + 8
371+ loop(pos < rb) {
372+ local ch = out.substring(pos, pos+1)
373+ if ch >= "0" && ch <= "9" {
374+ local digits = me._read_digits(out, pos)
375+ // materialize this operand if needed
376+ if digits != "" {
377+ // Check local def
378+ local has_def = 0
379+ local def_pat = "\"dst\":" + digits
380+ local s = insts_start
381+ loop(true) {
382+ local p = me._index_of_from(out, def_pat, s)
383+ if p < 0 || p >= obj_start { break }
384+ has_def = 1
385+ break
386+ }
387+ if has_def == 0 {
388+ local new_id = me._max_dst_id(out) + 1
389+ local copy_inst = "{\\\"op\\\":\\\"copy\\\",\\\"src\\\":" + digits + ",\\\"dst\\\":" + new_id + "},"
390+ out = out.substring(0, insert_at) + copy_inst + out.substring(insert_at, out.length())
391+ local delta = copy_inst.length()
392+ if insert_at <= obj_start { obj_start = obj_start + delta obj_end = obj_end + delta }
393+ // rewrite occurrence inside [ak..rb)
394+ local head = out.substring(0, ak)
395+ local mid = out.substring(ak, rb)
396+ local q = mid.indexOf(digits)
397+ if q >= 0 {
398+ mid = mid.substring(0, q) + new_id + mid.substring(q + digits.length(), mid.length())
399+ out = head + mid + out.substring(rb, out.length())
400+ // recompute bounds
401+ insts_start = me._block_insts_start(out, obj_start)
402+ insert_at = insts_start
403+ scan = insts_start
404+ loop(true) {
405+ local pphi2 = me._index_of_from(out, "\"op\":\"phi\"", scan)
406+ if pphi2 < 0 || pphi2 >= obj_start { break }
407+ local sphi2 = me._seek_obj_start(out, pphi2)
408+ local ephi2 = me._seek_obj_end(out, sphi2)
409+ insert_at = ephi2
410+ scan = ephi2
411+ }
412+ // refresh rb & pos relative to new string
413+ rb = me._index_of_from(out, "]", ak + 8)
414+ if rb < 0 { rb = obj_end }
415+ pos = ak + 8
416+ continue
417+ }
418+ }
419+ }
420+ // Move after this number (recompute rb due to potential rewrite)
421+ rb = me._index_of_from(out, "]", ak + 8)
422+ if rb < 0 { rb = obj_end }
423+ pos = ak + 8
424+ // advance to next non-digit to avoid infinite loop
425+ loop(true) {
426+ local c2 = out.substring(pos, pos+1)
427+ if !(c2 >= "0" && c2 <= "9") { break }
428+ pos = pos + 1
429+ }
430+ } else {
431+ pos = pos + 1
432+ }
433+ }
434+ }
435+ } else {
436+ // V1: mir_call has args in nested object
437+ local mk = "\"mir_call\":{"
438+ local mp = me._index_of_from(out, mk, obj_start)
439+ if mp >= 0 && mp < obj_end {
440+ local ak = me._index_of_from(out, "\"args\":[", mp)
441+ if ak >= 0 && ak < obj_end {
442+ local i = ak + 8
443+ local rb = i
444+ loop(true) {
445+ local ch = out.substring(i, i+1)
446+ if ch == "]" || ch == "" { rb = i break }
447+ i = i + 1
448+ }
449+ local pos = ak + 8
450+ loop(pos < rb) {
451+ local ch = out.substring(pos, pos+1)
452+ if ch >= "0" && ch <= "9" {
453+ local digits = me._read_digits(out, pos)
454+ // materialize this operand if needed
455+ if digits != "" {
456+ local has_def = 0
457+ local def_pat = "\"dst\":" + digits
458+ local s = insts_start
459+ loop(true) {
460+ local p = me._index_of_from(out, def_pat, s)
461+ if p < 0 || p >= obj_start { break }
462+ has_def = 1
463+ break
464+ }
465+ if has_def == 0 {
466+ local new_id = me._max_dst_id(out) + 1
467+ local copy_inst = "{\\\"op\\\":\\\"copy\\\",\\\"src\\\":" + digits + ",\\\"dst\\\":" + new_id + "},"
468+ out = out.substring(0, insert_at) + copy_inst + out.substring(insert_at, out.length())
469+ local delta = copy_inst.length()
470+ if insert_at <= obj_start { obj_start = obj_start + delta obj_end = obj_end + delta }
471+ local head = out.substring(0, ak)
472+ local mid = out.substring(ak, rb)
473+ local q = mid.indexOf(digits)
474+ if q >= 0 {
475+ mid = mid.substring(0, q) + new_id + mid.substring(q + digits.length(), mid.length())
476+ out = head + mid + out.substring(rb, out.length())
477+ insts_start = me._block_insts_start(out, obj_start)
478+ insert_at = insts_start
479+ scan = insts_start
480+ loop(true) {
481+ local pphi2 = me._index_of_from(out, "\"op\":\"phi\"", scan)
482+ if pphi2 < 0 || pphi2 >= obj_start { break }
483+ local sphi2 = me._seek_obj_start(out, pphi2)
484+ local ephi2 = me._seek_obj_end(out, sphi2)
485+ insert_at = ephi2
486+ scan = ephi2
487+ }
488+ rb = me._index_of_from(out, "]", ak + 8)
489+ if rb < 0 { rb = obj_end }
490+ pos = ak + 8
491+ continue
492+ }
493+ }
494+ }
495+ rb = me._index_of_from(out, "]", ak + 8)
496+ if rb < 0 { rb = obj_end }
497+ pos = ak + 8
498+ loop(true) {
499+ local c2 = out.substring(pos, pos+1)
500+ if !(c2 >= "0" && c2 <= "9") { break }
501+ pos = pos + 1
502+ }
503+ } else {
504+ pos = pos + 1
505+ }
506+ }
507+ }
508+ }
509+ }
510+
511+ // advance cursor beyond this instruction object
512+ cur = obj_end
513+ }
514+ return out
515+ }
516+
261517 // --- Trace API (既定OFF) ---
262518 trace_enable(on) {
263519 if on { me._trace_on = 1 } else { me._trace_on = 0 }
0 commit comments