///|
#valtype
priv struct PassLowerBranchlessSummary {
  has_candidate : Bool
  branchless : Bool
  has_children : Bool
}

///|
#inline
fn pass_lower_instruction_transfers_control(instr : @lib.Instruction) -> Bool {
  match instr {
    @lib.Br(_)
    | @lib.BrIf(_)
    | @lib.BrTable(_, _)
    | @lib.BrOnNull(_)
    | @lib.BrOnNonNull(_)
    | @lib.BrOnCast(_, _, _, _)
    | @lib.BrOnCastFail(_, _, _, _)
    | @lib.BrOnCastDescEq(_, _, _)
    | @lib.BrOnCastDescEqFail(_, _, _)
    | @lib.Return
    | @lib.ReturnCall(_)
    | @lib.ReturnCallIndirect(_, _)
    | @lib.ReturnCallRef(_)
    | @lib.Unreachable
    | @lib.Try(_, _, _, _)
    | @lib.TryTable(_, _, _)
    | @lib.Throw(_)
    | @lib.ThrowRef
    | @lib.Rethrow(_)
    | @lib.Suspend(_)
    | @lib.Resume(_, _)
    | @lib.ResumeThrow(_, _, _)
    | @lib.ResumeThrowRef(_, _)
    | @lib.StackSwitch(_, _) => true
    _ => false
  }
}

///|
// Removing a branchless Block cannot remove or add a transfer instruction.
// If no candidate is found, branchless is the old worker's exact result.
// Once a candidate is found, branchless is conservatively false; ancestors
// already have a candidate and do not need its exact branchless result.
// Keep invalid headers and option-dependent Blocks as conservative candidates;
// only the existing worker expands their signatures and decides to flatten.
fn pass_lower_branchless_summary(
  body : Array[@lib.Instruction],
  visits~ : Array[Int32]? = None,
) -> PassLowerBranchlessSummary {
  let mut branchless = true
  let mut has_children = false
  let mut visited = 0
  for instr in body {
    visited += 1
    let child : PassLowerBranchlessSummary = match instr {
      @lib.Block(_, @lib.Expr(child)) => {
        has_children = true
        let child = pass_lower_branchless_summary(child, visits~)
        {
          has_candidate: child.has_candidate || child.branchless,
          branchless: child.branchless,
          has_children: false,
        }
      }
      @lib.Loop(_, @lib.Expr(child)) => {
        has_children = true
        pass_lower_branchless_summary(child, visits~)
      }
      @lib.If(_, yes, no) => {
        has_children = true
        let yes = pass_lower_branchless_summary(yes, visits~)
        let no = if yes.has_candidate {
          { has_candidate: false, branchless: false, has_children: false, }
        } else {
          match no {
            Some(no) => pass_lower_branchless_summary(no, visits~)
            None =>
              { has_candidate: false, branchless: true, has_children: false, }
          }
        }
        {
          has_candidate: yes.has_candidate || no.has_candidate,
          branchless: yes.branchless && no.branchless,
          has_children: false,
        }
      }
      @lib.TryTable(_, _, @lib.Expr(child)) => {
        has_children = true
        let child = pass_lower_branchless_summary(child, visits~)
        {
          has_candidate: child.has_candidate,
          branchless: false,
          has_children: false,
        }
      }
      @lib.Try(_, @lib.Expr(child), catches, _) => {
        has_children = true
        let child = pass_lower_branchless_summary(child, visits~)
        let mut candidate = child.has_candidate
        if !candidate {
          for catch_ in catches {
            let @lib.Expr(child) = match catch_ {
              @lib.LegacyCatch(_, child) | @lib.LegacyCatchAll(child) => child
            }
            if pass_lower_branchless_summary(child, visits~).has_candidate {
              candidate = true
              break
            }
          }
        }
        { has_candidate: candidate, branchless: false, has_children: false, }
      }
      _ =>
        {
          has_candidate: false,
          branchless: !pass_lower_instruction_transfers_control(instr),
          has_children: false,
        }
    }
    if child.has_candidate {
      match visits {
        Some(visits) => visits[0] += visited
        None => ()
      }
      return { has_candidate: true, branchless: false, has_children, }
    }
    branchless = branchless && child.branchless
  }
  match visits {
    Some(visits) => visits[0] += visited
    None => ()
  }
  { has_candidate: false, branchless, has_children, }
}

///|
#inline
fn pass_lower_branchless_leaf(instr : @lib.Instruction) -> Bool {
  match instr {
    @lib.Block(_, _)
    | @lib.Loop(_, _)
    | @lib.If(_, _, _)
    | @lib.Try(_, _, _, _)
    | @lib.TryTable(_, _, _) => false
    _ => !pass_lower_instruction_transfers_control(instr)
  }
}