///| Compiler facts are advisory metadata. They never change core WebAssembly

///| validation or execution semantics unless an optimizer invocation explicitly

///|
/// chooses to trust semantic assertions after structural validation.
pub struct CompilerFactCustomSection {
  version : UInt
  module_fingerprint : Bytes?
  facts : OptimizationFactsSec
} derive(Eq, Hash, Debug)

///|
pub fn CompilerFactCustomSection::new(
  facts : OptimizationFactsSec,
  version? : UInt = 1U,
  module_fingerprint? : Bytes? = None,
) -> CompilerFactCustomSection {
  { version, module_fingerprint, facts, }
}

///|
pub struct ProducerInfo {
  name : String
  version : String
  build_id : String?
} derive(Eq, Hash, Debug)

///|
pub fn ProducerInfo::new(
  name : String,
  version : String,
  build_id? : String? = None,
) -> ProducerInfo {
  { name, version, build_id, }
}

///|
pub enum WorldMode {
  OpenWorld
  ClosedModule
  ClosedProgram
} derive(Eq, Hash, Debug)

///|
pub fn WorldMode::open_world() -> WorldMode {
  OpenWorld
}

///|
pub fn WorldMode::closed_module() -> WorldMode {
  ClosedModule
}

///|
pub fn WorldMode::closed_program() -> WorldMode {
  ClosedProgram
}

///|
pub struct WorldFacts {
  mode : WorldMode
  host_can_call_exports : Bool
  host_can_mutate_exported_state : Bool
} derive(Eq, Hash, Debug)

///|
pub fn WorldFacts::new(
  mode : WorldMode,
  host_can_call_exports : Bool,
  host_can_mutate_exported_state : Bool,
) -> WorldFacts {
  { mode, host_can_call_exports, host_can_mutate_exported_state, }
}

///|
pub fn WorldFacts::open_world(
  host_can_call_exports? : Bool = true,
  host_can_mutate_exported_state? : Bool = true,
) -> WorldFacts {
  WorldFacts::new(
    WorldMode::open_world(),
    host_can_call_exports,
    host_can_mutate_exported_state,
  )
}

///|
pub fn WorldFacts::closed_module(
  host_can_call_exports? : Bool = true,
  host_can_mutate_exported_state? : Bool = true,
) -> WorldFacts {
  WorldFacts::new(
    WorldMode::closed_module(),
    host_can_call_exports,
    host_can_mutate_exported_state,
  )
}

///|
pub fn WorldFacts::closed_program(
  host_can_call_exports? : Bool = true,
  host_can_mutate_exported_state? : Bool = false,
) -> WorldFacts {
  WorldFacts::new(
    WorldMode::closed_program(),
    host_can_call_exports,
    host_can_mutate_exported_state,
  )
}

///|
pub struct OptimizationFactsSec {
  producer : ProducerInfo?
  world : WorldFacts?
  functions : Array[FunctionFacts]
  signatures : Array[SignatureFacts]
  types : Array[TypeFacts]
  globals : Array[GlobalFacts]
  tables : Array[TableFacts]
  bodies : Array[FunctionBodyFacts]
  hints : Array[OptimizationHint]
} derive(Eq, Hash, Debug)

///|
pub fn OptimizationFactsSec::new(
  producer? : ProducerInfo? = None,
  world? : WorldFacts? = None,
  functions? : Array[FunctionFacts] = [],
  signatures? : Array[SignatureFacts] = [],
  types? : Array[TypeFacts] = [],
  globals? : Array[GlobalFacts] = [],
  tables? : Array[TableFacts] = [],
  bodies? : Array[FunctionBodyFacts] = [],
  hints? : Array[OptimizationHint] = [],
) -> OptimizationFactsSec {
  {
    producer,
    world,
    functions,
    signatures,
    types,
    globals,
    tables,
    bodies,
    hints,
  }
}

///|
pub struct OptimizationFactsBuilder {
  mut producer : ProducerInfo?
  mut world : WorldFacts?
  functions : Array[FunctionFacts]
  signatures : Array[SignatureFacts]
  types : Array[TypeFacts]
  globals : Array[GlobalFacts]
  tables : Array[TableFacts]
  bodies : Array[FunctionBodyFacts]
  hints : Array[OptimizationHint]
} derive(Eq, Hash, Debug)

///|
pub fn OptimizationFactsBuilder::new() -> OptimizationFactsBuilder {
  {
    producer: None,
    world: None,
    functions: [],
    signatures: [],
    types: [],
    globals: [],
    tables: [],
    bodies: [],
    hints: [],
  }
}

///|
pub fn OptimizationFactsBuilder::set_producer(
  self : OptimizationFactsBuilder,
  producer : ProducerInfo,
) -> Unit {
  self.producer = Some(producer)
}

///|
pub fn OptimizationFactsBuilder::set_world(
  self : OptimizationFactsBuilder,
  world : WorldFacts,
) -> Unit {
  self.world = Some(world)
}

///|
pub fn OptimizationFactsBuilder::add_function(
  self : OptimizationFactsBuilder,
  facts : FunctionFacts,
) -> Unit {
  self.functions.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_signature(
  self : OptimizationFactsBuilder,
  facts : SignatureFacts,
) -> Unit {
  self.signatures.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_type(
  self : OptimizationFactsBuilder,
  facts : TypeFacts,
) -> Unit {
  self.types.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_global(
  self : OptimizationFactsBuilder,
  facts : GlobalFacts,
) -> Unit {
  self.globals.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_table(
  self : OptimizationFactsBuilder,
  facts : TableFacts,
) -> Unit {
  self.tables.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_body(
  self : OptimizationFactsBuilder,
  facts : FunctionBodyFacts,
) -> Unit {
  self.bodies.push(facts)
}

///|
pub fn OptimizationFactsBuilder::add_hint(
  self : OptimizationFactsBuilder,
  hint : OptimizationHint,
) -> Unit {
  self.hints.push(hint)
}

///|
pub fn OptimizationFactsBuilder::finish(
  self : OptimizationFactsBuilder,
) -> OptimizationFactsSec {
  OptimizationFactsSec::new(
    producer=self.producer,
    world=self.world,
    functions=self.functions.copy(),
    signatures=self.signatures.copy(),
    types=self.types.copy(),
    globals=self.globals.copy(),
    tables=self.tables.copy(),
    bodies=self.bodies.copy(),
    hints=self.hints.copy(),
  )
}

///|
pub struct CodeSite {
  function : UInt
  offset : UInt
} derive(Eq, Hash, Debug)

///|
pub fn CodeSite::new(function : UInt, offset : UInt) -> CodeSite {
  { function, offset, }
}

///|
pub struct ValueSite {
  site : CodeSite
  result : UInt
} derive(Eq, Hash, Debug)

///|
pub fn ValueSite::new(site : CodeSite, result? : UInt = 0U) -> ValueSite {
  { site, result, }
}

///|
pub struct SourceProvenance {
  module_id : UInt64?
  declaration_id : UInt64?
  body_id : UInt64?
  expression_id : UInt64?
  source_offset : UInt64?
  specialization_id : UInt64?
} derive(Eq, Hash, Debug)

///|
pub fn SourceProvenance::new(
  module_id? : UInt64? = None,
  declaration_id? : UInt64? = None,
  body_id? : UInt64? = None,
  expression_id? : UInt64? = None,
  source_offset? : UInt64? = None,
  specialization_id? : UInt64? = None,
) -> SourceProvenance {
  {
    module_id,
    declaration_id,
    body_id,
    expression_id,
    source_offset,
    specialization_id,
  }
}

///|
pub enum IntWidth {
  Int8
  Int16
  Int32
  Int64
} derive(Eq, Hash, Debug)

///|
pub fn IntWidth::i8() -> IntWidth {
  Int8
}

///|
pub fn IntWidth::i16() -> IntWidth {
  Int16
}

///|
pub fn IntWidth::i32() -> IntWidth {
  Int32
}

///|
pub fn IntWidth::i64() -> IntWidth {
  Int64
}

///|
pub fn IntWidth::bit_count(self : IntWidth) -> UInt {
  match self {
    Int8 => 8U
    Int16 => 16U
    Int32 => 32U
    Int64 => 64U
  }
}

///|
pub struct IntConstant {
  width : IntWidth
  bits : UInt64
} derive(Eq, Hash, Debug)

///|
pub fn IntConstant::new(width : IntWidth, bits : UInt64) -> IntConstant {
  { width, bits, }
}

///|
pub struct V128Bits {
  low : UInt64
  high : UInt64
} derive(Eq, Hash, Debug)

///|
pub fn V128Bits::new(low : UInt64, high : UInt64) -> V128Bits {
  { low, high, }
}

///|
pub enum FactHeapType {
  TypeIndex(UInt)
  Any
  Eq
  Struct
  Array
  I31
  Func
  Extern
  Exn
  Cont
  String
  None
  NoFunc
  NoExtern
  NoExn
  NoCont
} derive(Eq, Hash, Debug)

///|
pub fn FactHeapType::type_index(index : UInt) -> FactHeapType {
  TypeIndex(index)
}

///|
pub fn FactHeapType::any() -> FactHeapType {
  Any
}

///|
pub fn FactHeapType::eq() -> FactHeapType {
  Eq
}

///|
pub fn FactHeapType::struct_() -> FactHeapType {
  Struct
}

///|
pub fn FactHeapType::array() -> FactHeapType {
  Array
}

///|
pub fn FactHeapType::i31() -> FactHeapType {
  I31
}

///|
pub fn FactHeapType::func() -> FactHeapType {
  Func
}

///|
pub fn FactHeapType::extern_() -> FactHeapType {
  Extern
}

///|
pub fn FactHeapType::exn() -> FactHeapType {
  Exn
}

///|
pub fn FactHeapType::cont() -> FactHeapType {
  Cont
}

///|
pub fn FactHeapType::string() -> FactHeapType {
  String
}

///|
pub fn FactHeapType::none() -> FactHeapType {
  None
}

///|
pub fn FactHeapType::no_func() -> FactHeapType {
  NoFunc
}

///|
pub fn FactHeapType::no_extern() -> FactHeapType {
  NoExtern
}

///|
pub fn FactHeapType::no_exn() -> FactHeapType {
  NoExn
}

///|
pub fn FactHeapType::no_cont() -> FactHeapType {
  NoCont
}

///|
fn fact_heap_type_rank(value : FactHeapType) -> UInt {
  match value {
    TypeIndex(_) => 0U
    Any => 1U
    Eq => 2U
    Struct => 3U
    Array => 4U
    I31 => 5U
    Func => 6U
    Extern => 7U
    Exn => 8U
    Cont => 9U
    String => 10U
    None => 11U
    NoFunc => 12U
    NoExtern => 13U
    NoExn => 14U
    NoCont => 15U
  }
}

///|
fn fact_heap_type_less(left : FactHeapType, right : FactHeapType) -> Bool {
  let lr = fact_heap_type_rank(left)
  let rr = fact_heap_type_rank(right)
  if lr != rr {
    return lr < rr
  }
  match (left, right) {
    (TypeIndex(a), TypeIndex(b)) => a < b
    _ => false
  }
}

///|
fn normalize_heap_types(values : Array[FactHeapType]) -> Array[FactHeapType] {
  let out : Array[FactHeapType] = []
  for value in values {
    let mut inserted = false
    for i = 0; i < out.length(); i = i + 1 {
      if value == out[i] {
        inserted = true
        break
      }
      if fact_heap_type_less(value, out[i]) {
        out.insert(i, value)
        inserted = true
        break
      }
    }
    if !inserted {
      out.push(value)
    }
  }
  out
}

///|
pub enum FactValue {
  IntegerValue(IntConstant)
  F32Bits(UInt)
  F64Bits(UInt64)
  V128Value(V128Bits)
  RefNullValue(FactHeapType)
  RefFuncValue(UInt)
  ImmutableGlobalValue(UInt)
} derive(Eq, Hash, Debug)

///|
pub fn FactValue::integer(value : IntConstant) -> FactValue {
  IntegerValue(value)
}

///|
pub fn FactValue::f32_bits(bits : UInt) -> FactValue {
  F32Bits(bits)
}

///|
pub fn FactValue::f64_bits(bits : UInt64) -> FactValue {
  F64Bits(bits)
}

///|
pub fn FactValue::v128(value : V128Bits) -> FactValue {
  V128Value(value)
}

///|
pub fn FactValue::ref_null(heap : FactHeapType) -> FactValue {
  RefNullValue(heap)
}

///|
pub fn FactValue::ref_func(function : UInt) -> FactValue {
  RefFuncValue(function)
}

///|
pub fn FactValue::immutable_global(global : UInt) -> FactValue {
  ImmutableGlobalValue(global)
}

///|
fn int_width_rank(width : IntWidth) -> UInt {
  match width {
    Int8 => 0U
    Int16 => 1U
    Int32 => 2U
    Int64 => 3U
  }
}

///|
fn fact_value_rank(value : FactValue) -> UInt {
  match value {
    IntegerValue(_) => 0U
    F32Bits(_) => 1U
    F64Bits(_) => 2U
    V128Value(_) => 3U
    RefNullValue(_) => 4U
    RefFuncValue(_) => 5U
    ImmutableGlobalValue(_) => 6U
  }
}

///|
fn fact_value_less(left : FactValue, right : FactValue) -> Bool {
  let lr = fact_value_rank(left)
  let rr = fact_value_rank(right)
  if lr != rr {
    return lr < rr
  }
  match (left, right) {
    (IntegerValue(a), IntegerValue(b)) => {
      let aw = int_width_rank(a.width)
      let bw = int_width_rank(b.width)
      aw < bw || (aw == bw && a.bits < b.bits)
    }
    (F32Bits(a), F32Bits(b)) => a < b
    (F64Bits(a), F64Bits(b)) => a < b
    (V128Value(a), V128Value(b)) =>
      a.high < b.high || (a.high == b.high && a.low < b.low)
    (RefNullValue(a), RefNullValue(b)) => fact_heap_type_less(a, b)
    (RefFuncValue(a), RefFuncValue(b)) => a < b
    (ImmutableGlobalValue(a), ImmutableGlobalValue(b)) => a < b
    _ => false
  }
}

///|
fn normalize_fact_values(values : Array[FactValue]) -> Array[FactValue] {
  let out : Array[FactValue] = []
  for value in values {
    let mut inserted = false
    for i = 0; i < out.length(); i = i + 1 {
      if value == out[i] {
        inserted = true
        break
      }
      if fact_value_less(value, out[i]) {
        out.insert(i, value)
        inserted = true
        break
      }
    }
    if !inserted {
      out.push(value)
    }
  }
  out
}

///|
/// `Some(empty)` means impossible; `None` means unknown.
pub struct ClosedValueSet(Array[FactValue]) derive(Eq, Hash, Debug)

///|
pub fn ClosedValueSet::new(values : Array[FactValue]) -> ClosedValueSet {
  ClosedValueSet(normalize_fact_values(values))
}

///|
pub struct SignedRange {
  min : Int64
  max : Int64
} derive(Eq, Hash, Debug)

///|
pub fn SignedRange::new(min : Int64, max : Int64) -> SignedRange {
  { min, max, }
}

///|
pub struct UnsignedRange {
  min : UInt64
  max : UInt64
} derive(Eq, Hash, Debug)

///|
pub fn UnsignedRange::new(min : UInt64, max : UInt64) -> UnsignedRange {
  { min, max, }
}

///|
pub struct KnownBits {
  known_zero : UInt64
  known_one : UInt64
} derive(Eq, Hash, Debug)

///|
pub fn KnownBits::new(known_zero : UInt64, known_one : UInt64) -> KnownBits {
  { known_zero, known_one, }
}

///|
pub struct IntegerFacts {
  width : IntWidth
  unsigned_range : UnsignedRange?
  signed_range : SignedRange?
  known_bits : KnownBits?
  nonzero : Bool
} derive(Eq, Hash, Debug)

///|
pub fn IntegerFacts::new(
  width : IntWidth,
  unsigned_range? : UnsignedRange? = None,
  signed_range? : SignedRange? = None,
  known_bits? : KnownBits? = None,
  nonzero? : Bool = false,
) -> IntegerFacts {
  { width, unsigned_range, signed_range, known_bits, nonzero, }
}

///|
pub enum FloatWidth {
  Float32
  Float64
} derive(Eq, Hash, Debug)

///|
pub fn FloatWidth::f32() -> FloatWidth {
  Float32
}

///|
pub fn FloatWidth::f64() -> FloatWidth {
  Float64
}

///|
pub struct FloatClassMask(UInt) derive(Eq, Hash, Debug)

///|
pub fn FloatClassMask::from_bits(bits : UInt) -> FloatClassMask {
  FloatClassMask(bits)
}

///|
pub fn FloatClassMask::bits(self : FloatClassMask) -> UInt {
  self.0
}

///|
pub fn FloatClassMask::empty() -> FloatClassMask {
  FloatClassMask(0U)
}

///|
pub fn FloatClassMask::positive_zero() -> FloatClassMask {
  FloatClassMask(1U << 0)
}

///|
pub fn FloatClassMask::negative_zero() -> FloatClassMask {
  FloatClassMask(1U << 1)
}

///|
pub fn FloatClassMask::positive_normal() -> FloatClassMask {
  FloatClassMask(1U << 2)
}

///|
pub fn FloatClassMask::negative_normal() -> FloatClassMask {
  FloatClassMask(1U << 3)
}

///|
pub fn FloatClassMask::positive_subnormal() -> FloatClassMask {
  FloatClassMask(1U << 4)
}

///|
pub fn FloatClassMask::negative_subnormal() -> FloatClassMask {
  FloatClassMask(1U << 5)
}

///|
pub fn FloatClassMask::positive_infinity() -> FloatClassMask {
  FloatClassMask(1U << 6)
}

///|
pub fn FloatClassMask::negative_infinity() -> FloatClassMask {
  FloatClassMask(1U << 7)
}

///|
pub fn FloatClassMask::nan() -> FloatClassMask {
  FloatClassMask(1U << 8)
}

///|
pub fn FloatClassMask::all() -> FloatClassMask {
  FloatClassMask(0x1FFU)
}

///|
pub fn FloatClassMask::contains(
  self : FloatClassMask,
  other : FloatClassMask,
) -> Bool {
  (self.0 & other.0) == other.0
}

///|
pub fn FloatClassMask::union(
  self : FloatClassMask,
  other : FloatClassMask,
) -> FloatClassMask {
  FloatClassMask(self.0 | other.0)
}

///|
pub struct FloatFacts {
  width : FloatWidth
  possible_classes : FloatClassMask
} derive(Eq, Hash, Debug)

///|
pub fn FloatFacts::new(
  width : FloatWidth,
  possible_classes : FloatClassMask,
) -> FloatFacts {
  { width, possible_classes, }
}

///|
pub enum NullabilityFacts {
  NullOnly
  NonNull
} derive(Eq, Hash, Debug)

///|
pub fn NullabilityFacts::null_only() -> NullabilityFacts {
  NullOnly
}

///|
pub fn NullabilityFacts::non_null() -> NullabilityFacts {
  NonNull
}

///|
pub struct ClosedHeapTypeSet(Array[FactHeapType]) derive(Eq, Hash, Debug)

///|
pub fn ClosedHeapTypeSet::new(
  values : Array[FactHeapType],
) -> ClosedHeapTypeSet {
  ClosedHeapTypeSet(normalize_heap_types(values))
}

///|
pub enum HeapDomain {
  ExactRuntimeType(FactHeapType)
  SubtypesOf(FactHeapType)
  ClosedRuntimeTypes(ClosedHeapTypeSet)
} derive(Eq, Hash, Debug)

///|
pub fn HeapDomain::exact_runtime_type(type_ : FactHeapType) -> HeapDomain {
  ExactRuntimeType(type_)
}

///|
pub fn HeapDomain::subtypes_of(type_ : FactHeapType) -> HeapDomain {
  SubtypesOf(type_)
}

///|
pub fn HeapDomain::closed_runtime_types(
  types : ClosedHeapTypeSet,
) -> HeapDomain {
  ClosedRuntimeTypes(types)
}

///|
pub struct ReferenceFacts {
  nullability : NullabilityFacts?
  heap : HeapDomain?
  function_targets : ClosedFunctionSet?
} derive(Eq, Hash, Debug)

///|
pub fn ReferenceFacts::new(
  nullability? : NullabilityFacts? = None,
  heap? : HeapDomain? = None,
  function_targets? : ClosedFunctionSet? = None,
) -> ReferenceFacts {
  { nullability, heap, function_targets, }
}

///|
pub enum VectorLaneShape {
  I8x16
  I16x8
  I32x4
  I64x2
  F32x4
  F64x2
} derive(Eq, Hash, Debug)

///|
pub fn VectorLaneShape::i8x16() -> VectorLaneShape {
  I8x16
}

///|
pub fn VectorLaneShape::i16x8() -> VectorLaneShape {
  I16x8
}

///|
pub fn VectorLaneShape::i32x4() -> VectorLaneShape {
  I32x4
}

///|
pub fn VectorLaneShape::i64x2() -> VectorLaneShape {
  I64x2
}

///|
pub fn VectorLaneShape::f32x4() -> VectorLaneShape {
  F32x4
}

///|
pub fn VectorLaneShape::f64x2() -> VectorLaneShape {
  F64x2
}

///|
pub struct KnownV128Bits {
  known_zero : V128Bits
  known_one : V128Bits
} derive(Eq, Hash, Debug)

///|
pub fn KnownV128Bits::new(
  known_zero : V128Bits,
  known_one : V128Bits,
) -> KnownV128Bits {
  { known_zero, known_one, }
}

///|
pub struct VectorFacts {
  known_bits : KnownV128Bits?
  splat : FactValue?
  lane_shape : VectorLaneShape?
} derive(Eq, Hash, Debug)

///|
pub fn VectorFacts::new(
  known_bits? : KnownV128Bits? = None,
  splat? : FactValue? = None,
  lane_shape? : VectorLaneShape? = None,
) -> VectorFacts {
  { known_bits, splat, lane_shape, }
}

///|
pub struct ValueFacts {
  possible_values : ClosedValueSet?
  integer : IntegerFacts?
  float_ : FloatFacts?
  vector : VectorFacts?
  reference : ReferenceFacts?
} derive(Eq, Hash, Debug)

///|
pub fn ValueFacts::new(
  possible_values? : ClosedValueSet? = None,
  integer? : IntegerFacts? = None,
  float_? : FloatFacts? = None,
  vector? : VectorFacts? = None,
  reference? : ReferenceFacts? = None,
) -> ValueFacts {
  { possible_values, integer, float_, vector, reference, }
}

///|
pub fn ValueFacts::unknown() -> ValueFacts {
  ValueFacts::new()
}

///|
pub fn ValueFacts::impossible() -> ValueFacts {
  ValueFacts::new(possible_values=Some(ClosedValueSet::new([])))
}

///|
pub fn ValueFacts::exact_value(value : FactValue) -> ValueFacts {
  ValueFacts::new(possible_values=Some(ClosedValueSet::new([value])))
}

///|
pub fn ValueFacts::unsigned_range(
  min : UInt64,
  max : UInt64,
  width? : IntWidth = IntWidth::i64(),
) -> ValueFacts {
  ValueFacts::new(
    integer=Some(
      IntegerFacts::new(
        width,
        unsigned_range=Some(UnsignedRange::new(min, max)),
      ),
    ),
  )
}

///|
pub fn ValueFacts::signed_range(
  min : Int64,
  max : Int64,
  width? : IntWidth = IntWidth::i64(),
) -> ValueFacts {
  ValueFacts::new(
    integer=Some(
      IntegerFacts::new(width, signed_range=Some(SignedRange::new(min, max))),
    ),
  )
}

///|
pub fn ValueFacts::nonnull() -> ValueFacts {
  ValueFacts::new(
    reference=Some(
      ReferenceFacts::new(nullability=Some(NullabilityFacts::non_null())),
    ),
  )
}

///|
pub fn ValueFacts::nonnull_exact_ref(heap : FactHeapType) -> ValueFacts {
  ValueFacts::new(
    reference=Some(
      ReferenceFacts::new(
        nullability=Some(NullabilityFacts::non_null()),
        heap=Some(HeapDomain::exact_runtime_type(heap)),
      ),
    ),
  )
}

///|
pub fn ValueFacts::exact_ref_func(function : UInt) -> ValueFacts {
  ValueFacts::new(
    possible_values=Some(ClosedValueSet::new([FactValue::ref_func(function)])),
    reference=Some(
      ReferenceFacts::new(
        nullability=Some(NullabilityFacts::non_null()),
        heap=Some(HeapDomain::exact_runtime_type(FactHeapType::func())),
        function_targets=Some(ClosedFunctionSet::new([function])),
      ),
    ),
  )
}

///|
pub struct ParameterRef {
  function : UInt
  parameter : UInt
} derive(Eq, Hash, Debug)

///|
pub fn ParameterRef::new(function : UInt, parameter : UInt) -> ParameterRef {
  { function, parameter, }
}

///|
pub struct FunctionResultRef {
  function : UInt
  result : UInt
} derive(Eq, Hash, Debug)

///|
pub fn FunctionResultRef::new(
  function : UInt,
  result : UInt,
) -> FunctionResultRef {
  { function, result, }
}

///|
pub enum ValueRef {
  SiteValue(ValueSite)
  ParameterValue(ParameterRef)
  FunctionResultValue(FunctionResultRef)
  GlobalValue(UInt)
} derive(Eq, Hash, Debug)

///|
pub fn ValueRef::site(site : ValueSite) -> ValueRef {
  SiteValue(site)
}

///|
pub fn ValueRef::parameter(parameter : ParameterRef) -> ValueRef {
  ParameterValue(parameter)
}

///|
pub fn ValueRef::function_result(result : FunctionResultRef) -> ValueRef {
  FunctionResultValue(result)
}

///|
pub fn ValueRef::global(global : UInt) -> ValueRef {
  GlobalValue(global)
}

///|
pub enum AliasSource {
  ParameterAlias(UInt)
  GlobalAlias(UInt)
  SiteAlias(ValueSite)
} derive(Eq, Hash, Debug)

///|
pub fn AliasSource::parameter(parameter : UInt) -> AliasSource {
  ParameterAlias(parameter)
}

///|
pub fn AliasSource::global(global : UInt) -> AliasSource {
  GlobalAlias(global)
}

///|
pub fn AliasSource::site(site : ValueSite) -> AliasSource {
  SiteAlias(site)
}

///|
fn alias_source_rank(value : AliasSource) -> UInt {
  match value {
    ParameterAlias(_) => 0U
    GlobalAlias(_) => 1U
    SiteAlias(_) => 2U
  }
}

///|
fn alias_source_less(left : AliasSource, right : AliasSource) -> Bool {
  let lr = alias_source_rank(left)
  let rr = alias_source_rank(right)
  if lr != rr {
    return lr < rr
  }
  match (left, right) {
    (ParameterAlias(a), ParameterAlias(b)) | (GlobalAlias(a), GlobalAlias(b)) =>
      a < b
    (SiteAlias(a), SiteAlias(b)) =>
      a.site.function < b.site.function ||
      (
        a.site.function == b.site.function &&
        (
          a.site.offset < b.site.offset ||
          (a.site.offset == b.site.offset && a.result < b.result)
        )
      )
    _ => false
  }
}

///|
pub struct ClosedAliasSet(Array[AliasSource]) derive(Eq, Hash, Debug)

///|
pub fn ClosedAliasSet::new(values : Array[AliasSource]) -> ClosedAliasSet {
  let out : Array[AliasSource] = []
  for value in values {
    let mut inserted = false
    for i = 0; i < out.length(); i = i + 1 {
      if value == out[i] {
        inserted = true
        break
      }
      if alias_source_less(value, out[i]) {
        out.insert(i, value)
        inserted = true
        break
      }
    }
    if !inserted {
      out.push(value)
    }
  }
  ClosedAliasSet(out)
}

///|
pub enum ResultIdentityFacts {
  FreshReference
  ClosedAliases(ClosedAliasSet)
} derive(Eq, Hash, Debug)

///|
pub fn ResultIdentityFacts::fresh() -> ResultIdentityFacts {
  FreshReference
}

///|
pub fn ResultIdentityFacts::same_as_parameter(
  parameter : UInt,
) -> ResultIdentityFacts {
  ClosedAliases(ClosedAliasSet::new([AliasSource::parameter(parameter)]))
}

///|
pub fn ResultIdentityFacts::same_as_global(
  global : UInt,
) -> ResultIdentityFacts {
  ClosedAliases(ClosedAliasSet::new([AliasSource::global(global)]))
}

///|
pub fn ResultIdentityFacts::closed_aliases(
  aliases : ClosedAliasSet,
) -> ResultIdentityFacts {
  ClosedAliases(aliases)
}

///|
pub struct ParameterFacts {
  parameter : UInt
  value : ValueFacts?
  uses : ParameterUseMask?
  escape_sinks : EscapeSinkMask?
} derive(Eq, Hash, Debug)

///|
pub fn ParameterFacts::new(
  parameter : UInt,
  value? : ValueFacts? = None,
  uses? : ParameterUseMask? = None,
  escape_sinks? : EscapeSinkMask? = None,
) -> ParameterFacts {
  { parameter, value, uses, escape_sinks, }
}

///|
pub struct ResultFacts {
  result : UInt
  value : ValueFacts?
  identity : ResultIdentityFacts?
} derive(Eq, Hash, Debug)

///|
pub fn ResultFacts::new(
  result : UInt,
  value? : ValueFacts? = None,
  identity? : ResultIdentityFacts? = None,
) -> ResultFacts {
  { result, value, identity, }
}

///|
pub struct ParameterUseMask(UInt64) derive(Eq, Hash, Debug)

///|
pub fn ParameterUseMask::from_bits(bits : UInt64) -> ParameterUseMask {
  ParameterUseMask(bits)
}

///|
pub fn ParameterUseMask::bits(self : ParameterUseMask) -> UInt64 {
  self.0
}

///|
pub fn ParameterUseMask::empty() -> ParameterUseMask {
  ParameterUseMask(0UL)
}

///|
pub fn ParameterUseMask::direct_computation() -> ParameterUseMask {
  ParameterUseMask(1UL << 0)
}

///|
pub fn ParameterUseMask::control() -> ParameterUseMask {
  ParameterUseMask(1UL << 1)
}

///|
pub fn ParameterUseMask::address_index() -> ParameterUseMask {
  ParameterUseMask(1UL << 2)
}

///|
pub fn ParameterUseMask::call_argument() -> ParameterUseMask {
  ParameterUseMask(1UL << 3)
}

///|
pub fn ParameterUseMask::stored_value() -> ParameterUseMask {
  ParameterUseMask(1UL << 4)
}

///|
pub fn ParameterUseMask::return_() -> ParameterUseMask {
  ParameterUseMask(1UL << 5)
}

///|
pub fn ParameterUseMask::identity_observing() -> ParameterUseMask {
  ParameterUseMask(1UL << 6)
}

///|
pub fn ParameterUseMask::all_known() -> ParameterUseMask {
  ParameterUseMask(0x7FUL)
}

///|
pub fn ParameterUseMask::contains(
  self : ParameterUseMask,
  other : ParameterUseMask,
) -> Bool {
  (self.0 & other.0) == other.0
}

///|
pub fn ParameterUseMask::union(
  self : ParameterUseMask,
  other : ParameterUseMask,
) -> ParameterUseMask {
  ParameterUseMask(self.0 | other.0)
}

///|
pub struct EscapeSinkMask(UInt64) derive(Eq, Hash, Debug)

///|
pub fn EscapeSinkMask::from_bits(bits : UInt64) -> EscapeSinkMask {
  EscapeSinkMask(bits)
}

///|
pub fn EscapeSinkMask::bits(self : EscapeSinkMask) -> UInt64 {
  self.0
}

///|
pub fn EscapeSinkMask::empty() -> EscapeSinkMask {
  EscapeSinkMask(0UL)
}

///|
pub fn EscapeSinkMask::return_() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 0)
}

///|
pub fn EscapeSinkMask::call() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 1)
}

///|
pub fn EscapeSinkMask::global() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 2)
}

///|
pub fn EscapeSinkMask::memory() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 3)
}

///|
pub fn EscapeSinkMask::table() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 4)
}

///|
pub fn EscapeSinkMask::heap() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 5)
}

///|
pub fn EscapeSinkMask::throw_() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 6)
}

///|
pub fn EscapeSinkMask::host_unknown() -> EscapeSinkMask {
  EscapeSinkMask(1UL << 7)
}

///|
pub fn EscapeSinkMask::all_known() -> EscapeSinkMask {
  EscapeSinkMask(0xFFUL)
}

///|
pub fn EscapeSinkMask::contains(
  self : EscapeSinkMask,
  other : EscapeSinkMask,
) -> Bool {
  (self.0 & other.0) == other.0
}

///|
pub fn EscapeSinkMask::union(
  self : EscapeSinkMask,
  other : EscapeSinkMask,
) -> EscapeSinkMask {
  EscapeSinkMask(self.0 | other.0)
}

///| Effect bits are broad upper bounds. Optional resource sets are complete only

///|
/// when present; an absent set means unknown, never empty.
pub struct EffectMask(UInt64) derive(Eq, Hash, Debug)

///|
pub fn EffectMask::from_bits(bits : UInt64) -> EffectMask {
  EffectMask(bits)
}

///|
pub fn EffectMask::bits(self : EffectMask) -> UInt64 {
  self.0
}

///|
pub fn EffectMask::empty() -> EffectMask {
  EffectMask(0UL)
}

///|
pub fn EffectMask::may_trap() -> EffectMask {
  EffectMask(1UL << 0)
}

///|
pub fn EffectMask::may_throw() -> EffectMask {
  EffectMask(1UL << 1)
}

///|
pub fn EffectMask::may_diverge() -> EffectMask {
  EffectMask(1UL << 2)
}

///|
pub fn EffectMask::may_call_unknown_code() -> EffectMask {
  EffectMask(1UL << 3)
}

///|
pub fn EffectMask::may_call_host_code() -> EffectMask {
  EffectMask(1UL << 4)
}

///|
pub fn EffectMask::may_reenter() -> EffectMask {
  EffectMask(1UL << 5)
}

///|
pub fn EffectMask::may_suspend() -> EffectMask {
  EffectMask(1UL << 6)
}

///|
pub fn EffectMask::may_allocate_gc() -> EffectMask {
  EffectMask(1UL << 7)
}

///|
pub fn EffectMask::may_observe_reference_identity() -> EffectMask {
  EffectMask(1UL << 8)
}

///|
pub fn EffectMask::may_produce_nondeterministic_results() -> EffectMask {
  EffectMask(1UL << 9)
}

///|
pub fn EffectMask::may_read_global() -> EffectMask {
  EffectMask(1UL << 10)
}

///|
pub fn EffectMask::may_write_global() -> EffectMask {
  EffectMask(1UL << 11)
}

///|
pub fn EffectMask::may_read_memory() -> EffectMask {
  EffectMask(1UL << 12)
}

///|
pub fn EffectMask::may_write_memory() -> EffectMask {
  EffectMask(1UL << 13)
}

///|
pub fn EffectMask::may_grow_memory() -> EffectMask {
  EffectMask(1UL << 14)
}

///|
pub fn EffectMask::may_read_table() -> EffectMask {
  EffectMask(1UL << 15)
}

///|
pub fn EffectMask::may_write_table() -> EffectMask {
  EffectMask(1UL << 16)
}

///|
pub fn EffectMask::may_grow_table() -> EffectMask {
  EffectMask(1UL << 17)
}

///|
pub fn EffectMask::may_read_gc_heap() -> EffectMask {
  EffectMask(1UL << 18)
}

///|
pub fn EffectMask::may_write_gc_heap() -> EffectMask {
  EffectMask(1UL << 19)
}

///|
pub fn EffectMask::may_synchronize() -> EffectMask {
  EffectMask(1UL << 20)
}

///|
pub fn EffectMask::may_wait_or_notify() -> EffectMask {
  EffectMask(1UL << 21)
}

///|
pub fn EffectMask::local_state() -> EffectMask {
  EffectMask(1UL << 22)
}

///|
pub fn EffectMask::contains(self : EffectMask, other : EffectMask) -> Bool {
  (self.0 & other.0) == other.0
}

///|
pub fn EffectMask::union(self : EffectMask, other : EffectMask) -> EffectMask {
  EffectMask(self.0 | other.0)
}

///|
/// Compatibility conveniences for the pre-freeze API.
pub fn EffectMask::trap() -> EffectMask {
  EffectMask::may_trap()
}

///|
pub fn EffectMask::throw_() -> EffectMask {
  EffectMask::may_throw()
}

///|
pub fn EffectMask::call() -> EffectMask {
  EffectMask::may_call_unknown_code()
}

///|
pub fn EffectMask::allocation() -> EffectMask {
  EffectMask::may_allocate_gc()
}

///|
pub fn EffectMask::memory_read() -> EffectMask {
  EffectMask::may_read_memory()
}

///|
pub fn EffectMask::memory_write() -> EffectMask {
  EffectMask::may_write_memory()
}

///|
pub fn EffectMask::table_read() -> EffectMask {
  EffectMask::may_read_table()
}

///|
pub fn EffectMask::table_write() -> EffectMask {
  EffectMask::may_write_table()
}

///|
pub fn EffectMask::heap_read() -> EffectMask {
  EffectMask::may_read_gc_heap()
}

///|
pub fn EffectMask::heap_write() -> EffectMask {
  EffectMask::may_write_gc_heap()
}

///|
pub fn EffectMask::global_state() -> EffectMask {
  EffectMask::may_read_global().union(EffectMask::may_write_global())
}

///|
pub fn EffectMask::control() -> EffectMask {
  EffectMask::may_trap()
  .union(EffectMask::may_throw())
  .union(EffectMask::may_diverge())
}

///|
fn normalize_uint_set(values : Array[UInt]) -> Array[UInt] {
  let sorted = values.copy()
  sorted.sort()
  let out : Array[UInt] = []
  for value in sorted {
    if out.is_empty() || out[out.length() - 1] != value {
      out.push(value)
    }
  }
  out
}

///|
pub struct ClosedGlobalSet(Array[UInt]) derive(Eq, Hash, Debug)

///|
pub fn ClosedGlobalSet::new(values : Array[UInt]) -> ClosedGlobalSet {
  ClosedGlobalSet(normalize_uint_set(values))
}

///|
pub struct ClosedMemorySet(Array[UInt]) derive(Eq, Hash, Debug)

///|
pub fn ClosedMemorySet::new(values : Array[UInt]) -> ClosedMemorySet {
  ClosedMemorySet(normalize_uint_set(values))
}

///|
pub struct ClosedTableSet(Array[UInt]) derive(Eq, Hash, Debug)

///|
pub fn ClosedTableSet::new(values : Array[UInt]) -> ClosedTableSet {
  ClosedTableSet(normalize_uint_set(values))
}

///|
pub struct ClosedTypeViewSet(Array[UInt]) derive(Eq, Hash, Debug)

///|
pub fn ClosedTypeViewSet::new(values : Array[UInt]) -> ClosedTypeViewSet {
  ClosedTypeViewSet(normalize_uint_set(values))
}

///|
pub struct ClosedFunctionSet(Array[UInt]) derive(Eq, Hash, Debug)

///|
pub fn ClosedFunctionSet::new(values : Array[UInt]) -> ClosedFunctionSet {
  ClosedFunctionSet(normalize_uint_set(values))
}

///|
pub struct EffectFacts {
  may : EffectMask
  read_globals : ClosedGlobalSet?
  written_globals : ClosedGlobalSet?
  read_memories : ClosedMemorySet?
  written_memories : ClosedMemorySet?
  read_tables : ClosedTableSet?
  written_tables : ClosedTableSet?
  read_heap_types : ClosedTypeViewSet?
  written_heap_types : ClosedTypeViewSet?
  direct_callees : ClosedFunctionSet?
} derive(Eq, Hash, Debug)

///|
pub fn EffectFacts::new(
  may : EffectMask,
  read_globals? : ClosedGlobalSet? = None,
  written_globals? : ClosedGlobalSet? = None,
  read_memories? : ClosedMemorySet? = None,
  written_memories? : ClosedMemorySet? = None,
  read_tables? : ClosedTableSet? = None,
  written_tables? : ClosedTableSet? = None,
  read_heap_types? : ClosedTypeViewSet? = None,
  written_heap_types? : ClosedTypeViewSet? = None,
  direct_callees? : ClosedFunctionSet? = None,
) -> EffectFacts {
  {
    may,
    read_globals,
    written_globals,
    read_memories,
    written_memories,
    read_tables,
    written_tables,
    read_heap_types,
    written_heap_types,
    direct_callees,
  }
}

///|
pub struct FunctionBoundaryFacts {
  all_call_sites_known : Bool
  address_taken : Bool
  host_callable : Bool
} derive(Eq, Hash, Debug)

///|
pub fn FunctionBoundaryFacts::new(
  all_call_sites_known : Bool,
  address_taken : Bool,
  host_callable : Bool,
) -> FunctionBoundaryFacts {
  { all_call_sites_known, address_taken, host_callable, }
}

///|
pub struct FunctionProfile {
  entry_count : UInt64?
  temperature : TemperatureHint?
} derive(Eq, Hash, Debug)

///|
pub fn FunctionProfile::new(
  entry_count? : UInt64? = None,
  temperature? : TemperatureHint? = None,
) -> FunctionProfile {
  { entry_count, temperature, }
}

///|
pub struct CallProfile {
  count : UInt64?
  temperature : TemperatureHint?
} derive(Eq, Hash, Debug)

///|
pub fn CallProfile::new(
  count? : UInt64? = None,
  temperature? : TemperatureHint? = None,
) -> CallProfile {
  { count, temperature, }
}

///|
pub struct BranchProfile {
  taken : UInt64
  not_taken : UInt64
} derive(Eq, Hash, Debug)

///|
pub fn BranchProfile::new(taken : UInt64, not_taken : UInt64) -> BranchProfile {
  { taken, not_taken, }
}

///|
pub struct LoopProfile {
  backedge_count : UInt64?
  estimated_trip_count : UInt64?
} derive(Eq, Hash, Debug)

///|
pub fn LoopProfile::new(
  backedge_count? : UInt64? = None,
  estimated_trip_count? : UInt64? = None,
) -> LoopProfile {
  { backedge_count, estimated_trip_count, }
}

///|
pub enum TemperatureHint {
  Cold
  Normal
  Hot
} derive(Eq, Hash, Debug)

///|
pub fn TemperatureHint::cold() -> TemperatureHint {
  Cold
}

///|
pub fn TemperatureHint::normal() -> TemperatureHint {
  Normal
}

///|
pub fn TemperatureHint::hot() -> TemperatureHint {
  Hot
}

///|
/// Profitability hints are never semantic requirements.
pub enum InlinePolicy {
  DefaultInlinePolicy
  NeverInline
  PreferInline
  AlwaysInline
} derive(Eq, Hash, Debug)

///|
pub fn InlinePolicy::default_() -> InlinePolicy {
  DefaultInlinePolicy
}

///|
pub fn InlinePolicy::never() -> InlinePolicy {
  NeverInline
}

///|
pub fn InlinePolicy::prefer() -> InlinePolicy {
  PreferInline
}

///|
pub fn InlinePolicy::always() -> InlinePolicy {
  AlwaysInline
}

///|
pub struct FunctionFacts {
  function : UInt
  effects : EffectFacts?
  parameters : Array[ParameterFacts]
  results : Array[ResultFacts]
  boundary : FunctionBoundaryFacts?
  profile : FunctionProfile?
  inline_policy : InlinePolicy?
} derive(Eq, Hash, Debug)

///|
pub fn FunctionFacts::new(
  function : UInt,
  effects? : EffectFacts? = None,
  parameters? : Array[ParameterFacts] = [],
  results? : Array[ResultFacts] = [],
  boundary? : FunctionBoundaryFacts? = None,
  profile? : FunctionProfile? = None,
  inline_policy? : InlinePolicy? = None,
) -> FunctionFacts {
  { function, effects, parameters, results, boundary, profile, inline_policy, }
}

///|
pub struct IndexedValueFacts {
  index : UInt
  value : ValueFacts
} derive(Eq, Hash, Debug)

///|
pub fn IndexedValueFacts::new(
  index : UInt,
  value : ValueFacts,
) -> IndexedValueFacts {
  { index, value, }
}

///|
pub struct SignatureFacts {
  signature : UInt
  parameters : Array[IndexedValueFacts]
  results : Array[IndexedValueFacts]
  call_sites_complete : Bool
} derive(Eq, Hash, Debug)

///|
pub fn SignatureFacts::new(
  signature : UInt,
  parameters? : Array[IndexedValueFacts] = [],
  results? : Array[IndexedValueFacts] = [],
  call_sites_complete? : Bool = false,
) -> SignatureFacts {
  { signature, parameters, results, call_sites_complete, }
}

///|
pub enum TypeView {
  ExactTypeView(UInt)
  SubtypeClosureView(UInt)
} derive(Eq, Hash, Debug)

///|
pub fn TypeView::exact(type_ : UInt) -> TypeView {
  ExactTypeView(type_)
}

///|
pub fn TypeView::subtype_closure(type_ : UInt) -> TypeView {
  SubtypeClosureView(type_)
}

///|
pub enum TypePopulation {
  EmptyTypePopulation
  ClosedTypePopulation(ClosedHeapTypeSet)
} derive(Eq, Hash, Debug)

///|
pub fn TypePopulation::empty() -> TypePopulation {
  EmptyTypePopulation
}

///|
pub fn TypePopulation::closed(types : ClosedHeapTypeSet) -> TypePopulation {
  ClosedTypePopulation(types)
}

///|
fn code_site_less(left : CodeSite, right : CodeSite) -> Bool {
  left.function < right.function ||
  (left.function == right.function && left.offset < right.offset)
}

///|
fn normalize_code_sites(values : Array[CodeSite]) -> Array[CodeSite] {
  let out : Array[CodeSite] = []
  for value in values {
    let mut inserted = false
    for i = 0; i < out.length(); i = i + 1 {
      if value == out[i] {
        inserted = true
        break
      }
      if code_site_less(value, out[i]) {
        out.insert(i, value)
        inserted = true
        break
      }
    }
    if !inserted {
      out.push(value)
    }
  }
  out
}

///|
pub enum WriteFacts {
  NoWrites
  InitializationOnly
  ClosedWrites(Array[CodeSite])
} derive(Eq, Hash, Debug)

///|
pub fn WriteFacts::no_writes() -> WriteFacts {
  NoWrites
}

///|
pub fn WriteFacts::initialization_only() -> WriteFacts {
  InitializationOnly
}

///|
pub fn WriteFacts::closed(sites : Array[CodeSite]) -> WriteFacts {
  ClosedWrites(normalize_code_sites(sites))
}

///|
pub struct FieldUsageFacts {
  never_read : Bool
  never_written_after_initialization : Bool
} derive(Eq, Hash, Debug)

///|
pub fn FieldUsageFacts::new(
  never_read? : Bool = false,
  never_written_after_initialization? : Bool = false,
) -> FieldUsageFacts {
  { never_read, never_written_after_initialization, }
}

///|
pub struct FieldFacts {
  view : TypeView
  field : UInt
  stored_contents : ValueFacts?
  writes : WriteFacts?
  usage : FieldUsageFacts?
} derive(Eq, Hash, Debug)

///|
pub fn FieldFacts::new(
  view : TypeView,
  field : UInt,
  stored_contents? : ValueFacts? = None,
  writes? : WriteFacts? = None,
  usage? : FieldUsageFacts? = None,
) -> FieldFacts {
  { view, field, stored_contents, writes, usage, }
}

///|
pub struct ArrayElementFacts {
  view : TypeView
  stored_contents : ValueFacts?
  writes : WriteFacts?
} derive(Eq, Hash, Debug)

///|
pub fn ArrayElementFacts::new(
  view : TypeView,
  stored_contents? : ValueFacts? = None,
  writes? : WriteFacts? = None,
) -> ArrayElementFacts {
  { view, stored_contents, writes, }
}

///|
pub struct TypeFacts {
  type_ : UInt
  population : TypePopulation?
  fields : Array[FieldFacts]
  array_element : ArrayElementFacts?
} derive(Eq, Hash, Debug)

///|
pub fn TypeFacts::new(
  type_ : UInt,
  population? : TypePopulation? = None,
  fields? : Array[FieldFacts] = [],
  array_element? : ArrayElementFacts? = None,
) -> TypeFacts {
  { type_, population, fields, array_element, }
}

///|
pub enum MutationPhase {
  NeverMutated
  InitializationOnlyMutation
  RuntimeMutation
} derive(Eq, Hash, Debug)

///|
pub fn MutationPhase::never() -> MutationPhase {
  NeverMutated
}

///|
pub fn MutationPhase::initialization_only() -> MutationPhase {
  InitializationOnlyMutation
}

///|
pub fn MutationPhase::runtime() -> MutationPhase {
  RuntimeMutation
}

///|
pub struct MutationFacts {
  latest_phase : MutationPhase
  sites : Array[CodeSite]?
} derive(Eq, Hash, Debug)

///|
pub fn MutationFacts::new(
  latest_phase : MutationPhase,
  sites? : Array[CodeSite]? = None,
) -> MutationFacts {
  {
    latest_phase,
    sites: match sites {
      Some(values) => Some(normalize_code_sites(values))
      None => None
    },
  }
}

///|
pub struct GlobalFacts {
  global : UInt
  initial_value : ValueFacts?
  steady_state_value : ValueFacts?
  mutation : MutationFacts?
} derive(Eq, Hash, Debug)

///|
pub fn GlobalFacts::new(
  global : UInt,
  initial_value? : ValueFacts? = None,
  steady_state_value? : ValueFacts? = None,
  mutation? : MutationFacts? = None,
) -> GlobalFacts {
  { global, initial_value, steady_state_value, mutation, }
}

///|
pub struct TableEntryFacts {
  index : UInt64
  value : ReferenceFacts
} derive(Eq, Hash, Debug)

///|
pub fn TableEntryFacts::new(
  index : UInt64,
  value : ReferenceFacts,
) -> TableEntryFacts {
  { index, value, }
}

///|
fn normalize_table_entries(
  values : Array[TableEntryFacts],
) -> Array[TableEntryFacts] {
  let out : Array[TableEntryFacts] = []
  for value in values {
    let mut inserted = false
    for i = 0; i < out.length(); i = i + 1 {
      if value.index <= out[i].index {
        out.insert(i, value)
        inserted = true
        break
      }
    }
    if !inserted {
      out.push(value)
    }
  }
  out
}

///|
pub struct TableFacts {
  table : UInt
  mutation : MutationFacts?
  may_grow : Bool?
  default_value : ReferenceFacts?
  entries : Array[TableEntryFacts]
  contents_complete : Bool
} derive(Eq, Hash, Debug)

///|
pub fn TableFacts::new(
  table : UInt,
  mutation? : MutationFacts? = None,
  may_grow? : Bool? = None,
  default_value? : ReferenceFacts? = None,
  entries? : Array[TableEntryFacts] = [],
  contents_complete? : Bool = false,
) -> TableFacts {
  {
    table,
    mutation,
    may_grow,
    default_value,
    entries: normalize_table_entries(entries),
    contents_complete,
  }
}

///|
pub enum AllocationKind {
  StructAllocation(UInt)
  ArrayAllocation(UInt)
  ClosureAllocation(UInt)
  BoxAllocation(UInt)
  OtherGcAllocation(UInt)
} derive(Eq, Hash, Debug)

///|
pub fn AllocationKind::struct_(type_ : UInt) -> AllocationKind {
  StructAllocation(type_)
}

///|
pub fn AllocationKind::array(type_ : UInt) -> AllocationKind {
  ArrayAllocation(type_)
}

///|
pub fn AllocationKind::closure(type_ : UInt) -> AllocationKind {
  ClosureAllocation(type_)
}

///|
pub fn AllocationKind::box(type_ : UInt) -> AllocationKind {
  BoxAllocation(type_)
}

///|
pub fn AllocationKind::other_gc(type_ : UInt) -> AllocationKind {
  OtherGcAllocation(type_)
}

///|
pub struct AllocationFieldFacts {
  field : UInt
  no_post_init_writes : Bool
  stored_value : ValueFacts?
} derive(Eq, Hash, Debug)

///|
pub fn AllocationFieldFacts::new(
  field : UInt,
  no_post_init_writes? : Bool = false,
  stored_value? : ValueFacts? = None,
) -> AllocationFieldFacts {
  { field, no_post_init_writes, stored_value, }
}

///|
pub struct AllocationFacts {
  kind : AllocationKind
  escape_sinks : EscapeSinkMask?
  identity_unobserved : Bool
  thread_confined : Bool
  length : IntegerFacts?
  fields : Array[AllocationFieldFacts]
} derive(Eq, Hash, Debug)

///|
pub fn AllocationFacts::new(
  kind : AllocationKind,
  escape_sinks? : EscapeSinkMask? = None,
  identity_unobserved? : Bool = false,
  thread_confined? : Bool = false,
  length? : IntegerFacts? = None,
  fields? : Array[AllocationFieldFacts] = [],
) -> AllocationFacts {
  { kind, escape_sinks, identity_unobserved, thread_confined, length, fields, }
}

///|
pub struct AllocationSiteFacts {
  site : ValueSite
  facts : AllocationFacts
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn AllocationSiteFacts::new(
  site : ValueSite,
  facts : AllocationFacts,
  provenance? : SourceProvenance? = None,
) -> AllocationSiteFacts {
  { site, facts, provenance, }
}

///|
pub struct AccessFacts {
  in_bounds : Bool
  alignment_log2 : UInt?
} derive(Eq, Hash, Debug)

///|
pub fn AccessFacts::new(
  in_bounds? : Bool = false,
  alignment_log2? : UInt? = None,
) -> AccessFacts {
  { in_bounds, alignment_log2, }
}

///|
pub struct AccessSiteFacts {
  site : CodeSite
  facts : AccessFacts
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn AccessSiteFacts::new(
  site : CodeSite,
  facts : AccessFacts,
  provenance? : SourceProvenance? = None,
) -> AccessSiteFacts {
  { site, facts, provenance, }
}

///|
pub struct NumericOperationFacts {
  signed_no_wrap : Bool
  unsigned_no_wrap : Bool
  exact_division : Bool
} derive(Eq, Hash, Debug)

///|
pub fn NumericOperationFacts::new(
  signed_no_wrap? : Bool = false,
  unsigned_no_wrap? : Bool = false,
  exact_division? : Bool = false,
) -> NumericOperationFacts {
  { signed_no_wrap, unsigned_no_wrap, exact_division, }
}

///|
pub struct NumericOperationSiteFacts {
  site : ValueSite
  facts : NumericOperationFacts
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn NumericOperationSiteFacts::new(
  site : ValueSite,
  facts : NumericOperationFacts,
  provenance? : SourceProvenance? = None,
) -> NumericOperationSiteFacts {
  { site, facts, provenance, }
}

///|
pub enum FactOperand {
  ValueOperand(ValueRef)
  ConstantOperand(FactValue)
} derive(Eq, Hash, Debug)

///|
pub fn FactOperand::value(value : ValueRef) -> FactOperand {
  ValueOperand(value)
}

///|
pub fn FactOperand::constant(value : FactValue) -> FactOperand {
  ConstantOperand(value)
}

///|
pub enum RelationKind {
  Equal
  NotEqual
  SignedLessThan
  SignedLessOrEqual
  UnsignedLessThan
  UnsignedLessOrEqual
  SameReference
  DistinctReferences
} derive(Eq, Hash, Debug)

///|
pub fn RelationKind::equal() -> RelationKind {
  Equal
}

///|
pub fn RelationKind::not_equal() -> RelationKind {
  NotEqual
}

///|
pub fn RelationKind::signed_less_than() -> RelationKind {
  SignedLessThan
}

///|
pub fn RelationKind::signed_less_or_equal() -> RelationKind {
  SignedLessOrEqual
}

///|
pub fn RelationKind::unsigned_less_than() -> RelationKind {
  UnsignedLessThan
}

///|
pub fn RelationKind::unsigned_less_or_equal() -> RelationKind {
  UnsignedLessOrEqual
}

///|
pub fn RelationKind::same_reference() -> RelationKind {
  SameReference
}

///|
pub fn RelationKind::distinct_references() -> RelationKind {
  DistinctReferences
}

///|
pub struct ScopedRelationFacts {
  at : CodeSite
  left : FactOperand
  kind : RelationKind
  right : FactOperand
} derive(Eq, Hash, Debug)

///|
pub fn ScopedRelationFacts::new(
  at : CodeSite,
  left : FactOperand,
  kind : RelationKind,
  right : FactOperand,
) -> ScopedRelationFacts {
  { at, left, kind, right, }
}

///|
pub struct ExpressionFact {
  value : ValueSite
  facts : ValueFacts
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn ExpressionFact::new(
  value : ValueSite,
  facts : ValueFacts,
  provenance? : SourceProvenance? = None,
) -> ExpressionFact {
  { value, facts, provenance, }
}

///|
pub struct CallSiteFacts {
  site : CodeSite
  direct_target : UInt?
  profile : CallProfile?
  inline_policy : InlinePolicy?
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn CallSiteFacts::new(
  site : CodeSite,
  direct_target? : UInt? = None,
  profile? : CallProfile? = None,
  inline_policy? : InlinePolicy? = None,
  provenance? : SourceProvenance? = None,
) -> CallSiteFacts {
  { site, direct_target, profile, inline_policy, provenance, }
}

///|
pub struct BranchFacts {
  site : CodeSite
  profile : BranchProfile?
  temperature : TemperatureHint?
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn BranchFacts::new(
  site : CodeSite,
  profile? : BranchProfile? = None,
  temperature? : TemperatureHint? = None,
  provenance? : SourceProvenance? = None,
) -> BranchFacts {
  { site, profile, temperature, provenance, }
}

///|
pub struct LoopFacts {
  site : CodeSite
  profile : LoopProfile?
  temperature : TemperatureHint?
  provenance : SourceProvenance?
} derive(Eq, Hash, Debug)

///|
pub fn LoopFacts::new(
  site : CodeSite,
  profile? : LoopProfile? = None,
  temperature? : TemperatureHint? = None,
  provenance? : SourceProvenance? = None,
) -> LoopFacts {
  { site, profile, temperature, provenance, }
}

///|
pub struct FunctionBodyFacts {
  function : UInt
  sites : Array[ExpressionFact]
  relations : Array[ScopedRelationFacts]
  calls : Array[CallSiteFacts]
  branches : Array[BranchFacts]
  loops : Array[LoopFacts]
  allocations : Array[AllocationSiteFacts]
  accesses : Array[AccessSiteFacts]
  numeric_operations : Array[NumericOperationSiteFacts]
} derive(Eq, Hash, Debug)

///|
pub fn FunctionBodyFacts::new(
  function : UInt,
  sites? : Array[ExpressionFact] = [],
  relations? : Array[ScopedRelationFacts] = [],
  calls? : Array[CallSiteFacts] = [],
  branches? : Array[BranchFacts] = [],
  loops? : Array[LoopFacts] = [],
  allocations? : Array[AllocationSiteFacts] = [],
  accesses? : Array[AccessSiteFacts] = [],
  numeric_operations? : Array[NumericOperationSiteFacts] = [],
) -> FunctionBodyFacts {
  {
    function,
    sites,
    relations,
    calls,
    branches,
    loops,
    allocations,
    accesses,
    numeric_operations,
  }
}

///|
pub enum OptimizationGoal {
  PreferSpeed
  Balanced
  PreferSize
} derive(Eq, Hash, Debug)

///|
pub fn OptimizationGoal::prefer_speed() -> OptimizationGoal {
  PreferSpeed
}

///|
pub fn OptimizationGoal::balanced() -> OptimizationGoal {
  Balanced
}

///|
pub fn OptimizationGoal::prefer_size() -> OptimizationGoal {
  PreferSize
}

///|
pub enum HintTarget {
  FunctionHintTarget(UInt)
  SiteHintTarget(CodeSite)
} derive(Eq, Hash, Debug)

///|
pub fn HintTarget::function(function : UInt) -> HintTarget {
  FunctionHintTarget(function)
}

///|
pub fn HintTarget::site(site : CodeSite) -> HintTarget {
  SiteHintTarget(site)
}

///|
/// Probability and confidence values use the inclusive integer domain 0..65535.
pub enum OptimizationHint {
  HotnessHint(HintTarget, TemperatureHint)
  InlineHint(HintTarget, InlinePolicy)
  GoalHint(HintTarget, OptimizationGoal)
  BranchProbabilityHint(CodeSite, UInt)
  EstimatedLoopTripsHint(CodeSite, UInt64)
  LikelyCallTargetHint(CodeSite, UInt, UInt)
  LikelyHeapTypeHint(ValueSite, FactHeapType, UInt)
} derive(Eq, Hash, Debug)

///|
pub fn OptimizationHint::hotness(
  target : HintTarget,
  temperature : TemperatureHint,
) -> OptimizationHint {
  HotnessHint(target, temperature)
}

///|
pub fn OptimizationHint::inline_(
  target : HintTarget,
  policy : InlinePolicy,
) -> OptimizationHint {
  InlineHint(target, policy)
}

///|
pub fn OptimizationHint::goal(
  target : HintTarget,
  goal : OptimizationGoal,
) -> OptimizationHint {
  GoalHint(target, goal)
}

///|
pub fn OptimizationHint::branch_probability(
  site : CodeSite,
  probability : UInt,
) -> OptimizationHint {
  BranchProbabilityHint(site, probability)
}

///|
pub fn OptimizationHint::estimated_loop_trips(
  site : CodeSite,
  trips : UInt64,
) -> OptimizationHint {
  EstimatedLoopTripsHint(site, trips)
}

///|
pub fn OptimizationHint::likely_call_target(
  site : CodeSite,
  function : UInt,
  confidence : UInt,
) -> OptimizationHint {
  LikelyCallTargetHint(site, function, confidence)
}

///|
pub fn OptimizationHint::likely_heap_type(
  site : ValueSite,
  type_ : FactHeapType,
  confidence : UInt,
) -> OptimizationHint {
  LikelyHeapTypeHint(site, type_, confidence)
}