///|
/// Machine-level calling-convention layout chosen by an embedding or frontend.
///
/// MachV treats this as data: target packages expose machine facts, while an
/// embedding chooses which registers implement its internal ABI.
pub(all) struct CallConventionLayout {
  context_arg : PReg
  user_arg_gprs : Array[PReg]
  arg_fprs : Array[PReg]
  ret_gprs : Array[PReg]
  ret_fprs : Array[PReg]
}

///|
pub fn CallConventionLayout::int_result_register_count(
  self : CallConventionLayout,
) -> Int {
  self.ret_gprs.length()
}

///|
pub fn CallConventionLayout::float_result_register_count(
  self : CallConventionLayout,
) -> Int {
  self.ret_fprs.length()
}

///|
pub fn CallConventionLayout::requires_extra_results_ptr_for_result_counts(
  self : CallConventionLayout,
  int_count : Int,
  float_count : Int,
) -> Bool {
  int_count > self.int_result_register_count() ||
  float_count > self.float_result_register_count()
}

///|
/// Stack slot size (bytes) for overflow arguments in this convention.
pub fn CallConventionLayout::stack_slot_size(
  self : CallConventionLayout,
  class : RegClass,
) -> Int {
  ignore(self)
  match class {
    Vector => 16
    _ => 8
  }
}

///|
/// Stack slot alignment (bytes) for overflow arguments in this convention.
pub fn CallConventionLayout::stack_slot_align(
  self : CallConventionLayout,
  class : RegClass,
) -> Int {
  ignore(self)
  match class {
    Vector => 16
    _ => 8
  }
}

///|
/// Compute overflow argument stack layout for this convention.
///
/// Layout: [int_overflow...][float_overflow...] with V128 aligned to 16.
/// Returned offsets are relative to the start of the overflow area.
pub fn CallConventionLayout::layout_overflow_stack(
  self : CallConventionLayout,
  int_overflow_count : Int,
  float_overflow_classes : Array[RegClass],
) -> (Array[Int], Array[Int], Int) {
  let int_offsets : Array[Int] = []
  for i in 0..