// The accesses that carry a memarg.
//
// Generated by the same rule as the nullary table -- type prefix, dot, the rest
// in snake case -- with one number beside each: the alignment the access
// naturally has, which is the one the format leaves unwritten. `align=` appears
// only when the source asked for something else, so an access and its natural
// alignment must be spelled together or the printer would write an `align=` on
// every load in the corpus.
//
// The lowering stores the LOG of the alignment, because that is what the binary
// encodes; the text writes the byte count, so the two differ by an exponent
// here and nowhere else.

///|
/// An index the format leaves out when it is the unnamed default: the memory
/// or table an access reaches, which is memory 0 or table 0 unless said
/// otherwise. A NAMED one is always written -- the name is what the source
/// wrote, and dropping it would change the text even though the index is 0.
fn opt_index(names : Map[Int, Bytes], k : Int) -> String {
  match names.get(k) {
    Some(b) => " " + ident(b)
    None => if k == 0 { "" } else { " " + k.to_string() }
  }
}

///|
fn memidx(names : @wasm_bin.Names, k : Int) -> String {
  opt_index(names.memories, k)
}

///|
/// A load or store: its mnemonic, its memory, and the immediates that differ
/// from the defaults.
fn memarg(
  name : String,
  mem : Int,
  align : Int,
  offset : Int64,
  natural : Int,
  names : @wasm_bin.Names,
) -> String {
  let out = StringBuilder::new()
  out.write_string(name + memidx(names, mem))
  if offset != 0L {
    // UNSIGNED: a memory64 offset fills the 64 bits, and the format writes the
    // number rather than the two's complement reading of it.
    out.write_string(" offset=" + offset.reinterpret_as_uint64().to_string())
  }
  if align != natural {
    out.write_string(" align=" + (1L << align).to_string())
  }
  out.to_string()
}

///|
/// The text name and immediates of an access that carries a memarg, or `None`
/// when the instruction is not one.
fn memarg_instr(i : @wasm_bin.Instruction, names : @wasm_bin.Names) -> String? {
  match i {
    I32Load(mem, align, offset) =>
      Some(memarg("i32.load", mem, align, offset, 2, names))
    I64Load(mem, align, offset) =>
      Some(memarg("i64.load", mem, align, offset, 3, names))
    F32Load(mem, align, offset) =>
      Some(memarg("f32.load", mem, align, offset, 2, names))
    F64Load(mem, align, offset) =>
      Some(memarg("f64.load", mem, align, offset, 3, names))
    I32Load8S(mem, align, offset) =>
      Some(memarg("i32.load8_s", mem, align, offset, 0, names))
    I32Load8U(mem, align, offset) =>
      Some(memarg("i32.load8_u", mem, align, offset, 0, names))
    I32Load16S(mem, align, offset) =>
      Some(memarg("i32.load16_s", mem, align, offset, 1, names))
    I32Load16U(mem, align, offset) =>
      Some(memarg("i32.load16_u", mem, align, offset, 1, names))
    I64Load8S(mem, align, offset) =>
      Some(memarg("i64.load8_s", mem, align, offset, 0, names))
    I64Load8U(mem, align, offset) =>
      Some(memarg("i64.load8_u", mem, align, offset, 0, names))
    I64Load16S(mem, align, offset) =>
      Some(memarg("i64.load16_s", mem, align, offset, 1, names))
    I64Load16U(mem, align, offset) =>
      Some(memarg("i64.load16_u", mem, align, offset, 1, names))
    I64Load32S(mem, align, offset) =>
      Some(memarg("i64.load32_s", mem, align, offset, 2, names))
    I64Load32U(mem, align, offset) =>
      Some(memarg("i64.load32_u", mem, align, offset, 2, names))
    I32Store(mem, align, offset) =>
      Some(memarg("i32.store", mem, align, offset, 2, names))
    I64Store(mem, align, offset) =>
      Some(memarg("i64.store", mem, align, offset, 3, names))
    F32Store(mem, align, offset) =>
      Some(memarg("f32.store", mem, align, offset, 2, names))
    F64Store(mem, align, offset) =>
      Some(memarg("f64.store", mem, align, offset, 3, names))
    I32Store8(mem, align, offset) =>
      Some(memarg("i32.store8", mem, align, offset, 0, names))
    I32Store16(mem, align, offset) =>
      Some(memarg("i32.store16", mem, align, offset, 1, names))
    I64Store8(mem, align, offset) =>
      Some(memarg("i64.store8", mem, align, offset, 0, names))
    I64Store16(mem, align, offset) =>
      Some(memarg("i64.store16", mem, align, offset, 1, names))
    I64Store32(mem, align, offset) =>
      Some(memarg("i64.store32", mem, align, offset, 2, names))
    V128Load(mem, align, offset) =>
      Some(memarg("v128.load", mem, align, offset, 4, names))
    V128Load8x8S(mem, align, offset) =>
      Some(memarg("v128.load8x8_s", mem, align, offset, 3, names))
    V128Load8x8U(mem, align, offset) =>
      Some(memarg("v128.load8x8_u", mem, align, offset, 3, names))
    V128Load16x4S(mem, align, offset) =>
      Some(memarg("v128.load16x4_s", mem, align, offset, 3, names))
    V128Load16x4U(mem, align, offset) =>
      Some(memarg("v128.load16x4_u", mem, align, offset, 3, names))
    V128Load32x2S(mem, align, offset) =>
      Some(memarg("v128.load32x2_s", mem, align, offset, 3, names))
    V128Load32x2U(mem, align, offset) =>
      Some(memarg("v128.load32x2_u", mem, align, offset, 3, names))
    V128Load8Splat(mem, align, offset) =>
      Some(memarg("v128.load8_splat", mem, align, offset, 0, names))
    V128Load16Splat(mem, align, offset) =>
      Some(memarg("v128.load16_splat", mem, align, offset, 1, names))
    V128Load32Splat(mem, align, offset) =>
      Some(memarg("v128.load32_splat", mem, align, offset, 2, names))
    V128Load64Splat(mem, align, offset) =>
      Some(memarg("v128.load64_splat", mem, align, offset, 3, names))
    V128Load32Zero(mem, align, offset) =>
      Some(memarg("v128.load32_zero", mem, align, offset, 2, names))
    V128Load64Zero(mem, align, offset) =>
      Some(memarg("v128.load64_zero", mem, align, offset, 3, names))
    V128Store(mem, align, offset) =>
      Some(memarg("v128.store", mem, align, offset, 4, names))
    V128Load8Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.load8_lane", mem, align, offset, 0, names) +
        " " +
        lane.to_string(),
      )
    V128Load16Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.load16_lane", mem, align, offset, 1, names) +
        " " +
        lane.to_string(),
      )
    V128Load32Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.load32_lane", mem, align, offset, 2, names) +
        " " +
        lane.to_string(),
      )
    V128Load64Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.load64_lane", mem, align, offset, 3, names) +
        " " +
        lane.to_string(),
      )
    V128Store8Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.store8_lane", mem, align, offset, 0, names) +
        " " +
        lane.to_string(),
      )
    V128Store16Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.store16_lane", mem, align, offset, 1, names) +
        " " +
        lane.to_string(),
      )
    V128Store32Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.store32_lane", mem, align, offset, 2, names) +
        " " +
        lane.to_string(),
      )
    V128Store64Lane(mem, align, offset, lane) =>
      Some(
        memarg("v128.store64_lane", mem, align, offset, 3, names) +
        " " +
        lane.to_string(),
      )
    _ => None
  }
}