///|
fn Translator::continuation_signature(
  self : Translator,
  index : Int32,
) -> @types.FuncType {
  guard self.module_types[index].composite is Cont(function_index) else {
    abort("validated continuation type expected")
  }
  self.func_types[function_index]
}

///|
fn Translator::pop_continuation_arguments(
  self : Translator,
  count : Int32,
) -> Array[Value] {
  let arguments : Array[Value] = []
  for _ in 0.. Unit {
  let function = self.pop()
  self.push(
    @wasm_milkir.continuation_operation(self.builder, ContNew(index), [Ref], [
      function,
    ])[0],
  )
}

///|
fn Translator::translate_cont_bind(
  self : Translator,
  input : Int32,
  output : Int32,
) -> Unit {
  let reference = self.pop()
  let count = self.continuation_signature(input).params.length() -
    self.continuation_signature(output).params.length()
  let operands = [reference]
  operands.append(self.pop_continuation_arguments(count))
  self.push(
    @wasm_milkir.continuation_operation(
      self.builder,
      ContBind(input, output),
      [Ref],
      operands,
    )[0],
  )
}

///|
fn Translator::translate_cont_suspend(self : Translator, tag : Int32) -> Unit {
  let signature = self.func_types[self.tags[tag].type_idx]
  let arguments = self.pop_continuation_arguments(signature.params.length())
  self.spill_locals_if_in_try()
  for
    value in @wasm_milkir.continuation_operation(
      self.builder,
      ContSuspend(tag),
      signature.results[:].map(type_from_wasm),
      arguments,
    ) {
    self.push(value)
  }
}

///|
fn continuation_payload_words(types : Array[Type]) -> Int32 {
  let mut words = 0
  for ty in types {
    words += if ty == V128 { 2 } else { 1 }
  }
  words
}

///|
/// Resume produces a status plus disjoint result regions. Only the selected
/// handler edge consumes its region; the normal edge consumes function results.
fn Translator::translate_cont_resume(
  self : Translator,
  index : Int32,
  exception_tag : Int32,
  handlers : ArrayView[@types.ResumeHandler],
) -> Unit {
  let reference = self.pop()
  let signature = self.continuation_signature(index)
  let input_types : Array[@types.ValueType] = if exception_tag == -2 {
    signature.params[:].to_owned()
  } else if exception_tag == -1 {
    [ExnRef]
  } else {
    self.get_tag_param_types(exception_tag)
  }
  let operands = [reference]
  operands.append(self.pop_continuation_arguments(input_types.length()))
  let normal_types = signature.results.map(type_from_wasm)
  let result_types : Array[Type] = [I32]
  result_types.append(normal_types)
  let regions : Array[(Int32, Int32)] = []
  for handler in handlers {
    match handler {
      OnLabel(tag, depth) => {
        let types = self.get_tag_param_types(tag).map(type_from_wasm)
        types.push(Ref)
        let word_offset = continuation_payload_words(
          result_types[1:].to_owned(),
        )
        regions.push((result_types.length(), types.length()))
        operands.push(self.builder.iconst_i32(tag))
        operands.push(self.builder.iconst_i32(0))
        operands.push(self.builder.iconst_i32(word_offset))
        result_types.append(types)
        depth |> ignore
      }
      OnSwitch(tag) => {
        regions.push((0, 0))
        operands.push(self.builder.iconst_i32(tag))
        operands.push(self.builder.iconst_i32(1))
        operands.push(self.builder.iconst_i32(0))
      }
    }
  }
  self.spill_locals_if_in_try()
  let results = @wasm_milkir.continuation_operation(
    self.builder,
    ContResume(index, input_types.length(), handlers.length(), exception_tag),
    result_types,
    operands,
  )
  for handler_index, handler in handlers {
    if handler is OnLabel(_, depth) {
      let target_index = self.block_stack.length() - 1 - depth
      let region = regions[handler_index]
      let arguments : Array[Value] = []
      for i in 0..= 0 {
        let frame = self.block_stack[target_index]
        self.push_local_args(arguments, frame.local_params)
        self.mark_block_frame_reachable(target_index)
        self.branch_with_unwind(
          condition,
          frame.block,
          arguments,
          fallthrough,
          frame.handler_depth,
        )
      } else {
        let target = self.get_or_create_return_continuation()
        self.branch_with_unwind(condition, target, arguments, fallthrough, 0)
      }
      self.builder.switch_to_block(fallthrough)
    }
  }
  for i in 0.. Unit {
  let reference = self.pop()
  let signature = self.continuation_signature(index)
  let last = signature.params[signature.params.length() - 1]
  let other_index = match last {
    RefContTyped(index) | RefNullContTyped(index) => index
    _ => abort("validated switch continuation expected")
  }
  let operands = [reference]
  operands.append(
    self.pop_continuation_arguments(signature.params.length() - 1),
  )
  self.spill_locals_if_in_try()
  for
    value in @wasm_milkir.continuation_operation(
      self.builder,
      ContSwitch(index, tag),
      self.continuation_signature(other_index).params[:].map(type_from_wasm),
      operands,
    ) {
    self.push(value)
  }
}