// 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.
///|
fn il_import_targets(mod_ : @lib.Module) -> Array[Bool] {
let targets : Array[Bool] = []
match mod_.import_sec {
Some(@lib.ImportSec(imports)) =>
for import_ in imports {
match import_ {
@lib.Import(module_name, field_name, @lib.FuncExternType(_)) =>
targets.push(
module_name == @lib.Name::from_string("binaryen-intrinsics") &&
field_name == @lib.Name::from_string("call.without.effects"),
)
_ => ()
}
}
None => ()
}
targets
}
///|
fn il_local_target_type(
params : Array[@lib.ValType],
local : @lib.LocalIdx,
) -> @lib.TypeIdx? {
let @lib.LocalIdx(raw) = local
let index = raw.to_int()
if index < 0 || index >= params.length() {
return None
}
match params[index] {
@lib.RefTypeValType(@lib.HeapTypeRefType(_, _, @lib.HeapType(type_idx))) =>
Some(type_idx)
_ => None
}
}
///|
fn il_rewrite_expr(
expr : @lib.Expr,
targets : Array[Bool],
params : Array[@lib.ValType],
changed : Array[Bool],
) -> @lib.Expr {
let @lib.Expr(instrs) = expr
let out : Array[@lib.Instruction] = []
for instr in instrs {
match instr {
@lib.Block(block_type, body) =>
out.push(
@lib.Instruction::block(
block_type,
il_rewrite_expr(body, targets, params, changed),
),
)
@lib.Loop(block_type, body) =>
out.push(
@lib.Instruction::loop_(
block_type,
il_rewrite_expr(body, targets, params, changed),
),
)
@lib.If(block_type, then_body, else_body) => {
let @lib.Expr(next_then) = il_rewrite_expr(
@lib.Expr::new(then_body),
targets,
params,
changed,
)
let next_else = match else_body {
Some(body) => {
let @lib.Expr(next_body) = il_rewrite_expr(
@lib.Expr::new(body),
targets,
params,
changed,
)
Some(next_body)
}
None => None
}
out.push(@lib.Instruction::if_(block_type, next_then, next_else))
}
@lib.TryTable(block_type, catches, body) =>
out.push(
@lib.Instruction::try_table(
block_type,
catches,
il_rewrite_expr(body, targets, params, changed),
),
)
@lib.Try(block_type, body, catches, delegate) =>
out.push(
@lib.Instruction::try_(
block_type,
il_rewrite_expr(body, targets, params, changed),
catches.map(fn(catch_) {
match catch_ {
@lib.LegacyCatch(tag, catch_body) =>
@lib.LegacyCatch::tagged(
tag,
il_rewrite_expr(catch_body, targets, params, changed),
)
@lib.LegacyCatchAll(catch_body) =>
@lib.LegacyCatch::all(
il_rewrite_expr(catch_body, targets, params, changed),
)
}
}),
delegate~,
),
)
@lib.Call(target) | @lib.ReturnCall(target) => {
let @lib.FuncIdx(raw) = target
let index = raw.to_int()
if index < 0 ||
index >= targets.length() ||
!targets[index] ||
out.is_empty() {
out.push(instr)
continue
}
match out[out.length() - 1] {
@lib.RefFunc(callee) => {
ignore(out.pop())
if instr is @lib.ReturnCall(_) {
out.push(@lib.Instruction::return_call(callee))
} else {
out.push(@lib.Instruction::call(callee))
}
changed[0] = true
}
@lib.LocalGet(local) =>
match il_local_target_type(params, local) {
Some(type_idx) => {
if instr is @lib.ReturnCall(_) {
out.push(@lib.Instruction::return_call_ref(type_idx))
} else {
out.push(@lib.Instruction::call_ref(type_idx))
}
changed[0] = true
}
None => out.push(instr)
}
_ => out.push(instr)
}
}
_ => out.push(instr)
}
}
@lib.Expr::new(out)
}
///|
fn intrinsic_lowering_run_module_pass(mod_ : @lib.Module) -> @lib.Module {
let funcs = match mod_.code_sec {
Some(@lib.CodeSec(funcs)) => funcs
None => return mod_
}
let type_indices = match mod_.func_sec {
Some(@lib.FuncSec(type_indices)) => type_indices
None => return mod_
}
if type_indices.length() != funcs.length() {
return mod_
}
let targets = il_import_targets(mod_)
if !targets.any(fn(target) { target }) {
return mod_
}
let signatures = msf_build_type_signature_cache(mod_)
let changed = [false]
let rewritten : Array[@lib.Func] = []
for index, func in funcs {
let @lib.Func(locals, body) = func
let params = match msf_cached_signature(signatures, type_indices[index]) {
Some(@lib.FuncType(params, _)) => params
None => []
}
let local_types = params.copy()
for local_type in locals.iter() {
local_types.push(local_type)
}
rewritten.push(
@lib.Func::new(
locals,
il_rewrite_expr(body, targets, local_types, changed),
),
)
}
if !changed[0] {
return mod_
}
let candidate = mod_.with_code_sec(@lib.CodeSec::new(rewritten))
if @validate.validate_module(candidate).is_ok() {
candidate
} else {
mod_
}
}