// Licensing scope: see ../../LICENSE and this directory's LICENSE and NOTICE.
// Potentially incorporated Binaryen expression retains Apache-2.0 terms.
// Original Starshine contributions use the scoped MIT grant in ../../LICENSE.
// Modified for Starshine through 2026: implementation and validation use
// Starshine's representations and APIs; this is not an unchanged upstream file.
// Conservative notice retention does not establish copying or independence.
///|
// Only flat, non-terminating instructions qualify. Unknown control, tail calls
// and trapping producers retain the HOT dependency analysis.
fn dae2_flat_observed_arity(
instruction : @lib.Instruction,
graph : Dae2Graph,
infos : Array[Dae2Function],
indices : Array[@lib.TypeIdx],
type_infos : Array[Int32],
exposure : Array[DaeFunctionExposure],
) -> Int32 {
if dae_instr_is_droppable_pure_leaf(instruction) ||
dae_instr_is_nontrapping_pure_unary(instruction) ||
dae_instr_is_nontrapping_pure_binary(instruction) {
return 0
}
match instruction {
@lib.Nop
| @lib.Drop
| @lib.LocalSet(_)
| @lib.LocalTee(_)
| @lib.RefFunc(_) => 0
@lib.GlobalSet(_) => 1
@lib.I32Store(_)
| @lib.I64Store(_)
| @lib.F32Store(_)
| @lib.F64Store(_)
| @lib.I32Store8(_)
| @lib.I32Store16(_)
| @lib.I64Store8(_)
| @lib.I64Store16(_)
| @lib.I64Store32(_)
| @lib.TableSet(_)
| @lib.StructSet(_, _) => 2
@lib.ArraySet(_) => 3
@lib.Call(@lib.FuncIdx(raw)) => {
let callee = raw.reinterpret_as_int()
if callee < 0 || callee >= indices.length() {
return -1
}
if dae2_boundary_is_pinned(
graph, infos, callee, indices, type_infos, exposure,
) {
infos[callee].params.length()
} else {
0
}
}
@lib.CallIndirect(index, _) | @lib.CallRef(index) => {
let slot = dae2_type_slot(index, 0)
if slot < 0 || slot >= type_infos.length() {
return -1
}
let callee = type_infos[slot]
if callee < 0 || callee >= infos.length() {
return -1
}
if dae2_boundary_is_pinned(
graph, infos, callee, indices, type_infos, exposure,
) {
infos[callee].params.length() + 1
} else {
1
}
}
_ => -1
}
}
///|
// A readonly entry parameter directly consumed by an observable instruction is
// already live. Seeding that fact before lifting can pin a private void boundary
// and its callers. Qualification covers the entire body before adding seeds;
// any parameter assignment or unsupported control retains the normal analysis.
fn dae2_pin_flat_observable_params(
graph : Dae2Graph,
infos : Array[Dae2Function],
index : Int32,
body : Array[@lib.Instruction],
indices : Array[@lib.TypeIdx],
type_infos : Array[Int32],
exposure : Array[DaeFunctionExposure],
) -> Unit {
let info = infos[index]
if !info.results.is_empty() ||
info.params.is_empty() ||
dae2_boundary_is_pinned(graph, infos, index, indices, type_infos, exposure) {
return
}
let observations : Array[Int32] = []
let mut written : Array[Bool]? = None
for position = 0; position < body.length(); position = position + 1 {
let instruction = body[position]
let arity = dae2_flat_observed_arity(
instruction, graph, infos, indices, type_infos, exposure,
)
if arity < 0 {
return
}
if arity > 0 {
observations.push(position)
observations.push(arity)
}
match instruction {
@lib.LocalSet(@lib.LocalIdx(raw)) | @lib.LocalTee(@lib.LocalIdx(raw)) => {
let local = raw.reinterpret_as_int()
if local >= 0 && local < info.params.length() {
let flags = match written {
Some(flags) => flags
None => {
let flags = Array::make(info.params.length(), false)
written = Some(flags)
flags
}
}
flags[local] = true
}
}
_ => ()
}
}
for observation = 0
observation < observations.length()
observation = observation + 2 {
let mut producer = observations[observation] - 1
let mut remaining = observations[observation + 1]
while producer >= 0 && remaining > 0 {
let instruction = body[producer]
if !dae_instr_is_droppable_pure_leaf(instruction) {
break
}
match instruction {
@lib.LocalGet(@lib.LocalIdx(raw)) => {
let local = raw.reinterpret_as_int()
let unassigned = match written {
Some(flags) => local < 0 || local >= flags.length() || !flags[local]
None => true
}
if local >= 0 && local < info.params.length() && unassigned {
graph.observe(info.param_locations[local])
}
}
_ => ()
}
producer = producer - 1
remaining = remaining - 1
}
}
}