///|
/// Validate SIMD (V128) instructions
fn validate_instr_simd(
  ctx : ValidationContext,
  stack : OperandStack,
  instr : @types.Instruction,
) -> Unit raise ValidationError {
  match instr {
    // SIMD instructions
    V128Const(bytes) => {
      if bytes.length() != 16 {
        raise TypeMismatch("v128 constant requires 16 bytes")
      }
      stack.push(V128)
    }

    // SIMD load/store - natural alignments:
    // v128.load: 16 bytes = 4, load8x8/16x4/32x2: 8 bytes = 3
    // load8_splat: 0, load16_splat: 1, load32_splat/zero: 2, load64_splat/zero: 3
    V128Load(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 4 {
        raise InvalidAlignment(
          "v128.load alignment \{align} exceeds natural alignment 4",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load8x8S(memidx, align, offset)
    | V128Load8x8U(memidx, align, offset)
    | V128Load16x4S(memidx, align, offset)
    | V128Load16x4U(memidx, align, offset)
    | V128Load32x2S(memidx, align, offset)
    | V128Load32x2U(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 3 {
        raise InvalidAlignment(
          "v128.load_extend alignment \{align} exceeds natural alignment 3",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load8Splat(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 0 {
        raise InvalidAlignment(
          "v128.load8_splat alignment \{align} exceeds natural alignment 0",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load16Splat(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 1 {
        raise InvalidAlignment(
          "v128.load16_splat alignment \{align} exceeds natural alignment 1",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load32Splat(memidx, align, offset)
    | V128Load32Zero(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 2 {
        raise InvalidAlignment(
          "v128.load32 alignment \{align} exceeds natural alignment 2",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load64Splat(memidx, align, offset)
    | V128Load64Zero(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 3 {
        raise InvalidAlignment(
          "v128.load64 alignment \{align} exceeds natural alignment 3",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Store(memidx, align, offset) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 4 {
        raise InvalidAlignment(
          "v128.store alignment \{align} exceeds natural alignment 4",
        )
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
    }
    V128Load8Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 0 {
        raise InvalidAlignment(
          "v128.load8_lane alignment \{align} exceeds natural alignment 0",
        )
      }
      if lane < 0 || lane >= 16 {
        raise InvalidLaneIndex(lane, 16)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load16Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 1 {
        raise InvalidAlignment(
          "v128.load16_lane alignment \{align} exceeds natural alignment 1",
        )
      }
      if lane < 0 || lane >= 8 {
        raise InvalidLaneIndex(lane, 8)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load32Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 2 {
        raise InvalidAlignment(
          "v128.load32_lane alignment \{align} exceeds natural alignment 2",
        )
      }
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Load64Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 3 {
        raise InvalidAlignment(
          "v128.load64_lane alignment \{align} exceeds natural alignment 3",
        )
      }
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
      stack.push(V128)
    }
    V128Store8Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 0 {
        raise InvalidAlignment(
          "v128.store8_lane alignment \{align} exceeds natural alignment 0",
        )
      }
      if lane < 0 || lane >= 16 {
        raise InvalidLaneIndex(lane, 16)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
    }
    V128Store16Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 1 {
        raise InvalidAlignment(
          "v128.store16_lane alignment \{align} exceeds natural alignment 1",
        )
      }
      if lane < 0 || lane >= 8 {
        raise InvalidLaneIndex(lane, 8)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
    }
    V128Store32Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 2 {
        raise InvalidAlignment(
          "v128.store32_lane alignment \{align} exceeds natural alignment 2",
        )
      }
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
    }
    V128Store64Lane(memidx, align, offset, lane) => {
      if memidx < 0 || memidx >= ctx.mems.length() {
        raise InvalidMemoryIndex(memidx)
      }
      if align < 0 || align > 3 {
        raise InvalidAlignment(
          "v128.store64_lane alignment \{align} exceeds natural alignment 3",
        )
      }
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      validate_mem_offset(ctx.mems[memidx], offset)
      let mem = ctx.mems[memidx]
      stack.pop(V128)
      stack.pop(mem.addr_type())
    }

    // Shuffle/Swizzle - lane indices must be 0-31 (selecting from two concatenated v128s)
    I8x16Shuffle(lanes) => {
      if lanes.length() != 16 {
        raise TypeMismatch("shuffle requires 16 lanes")
      }
      for i = 0; i < 16; i = i + 1 {
        if lanes[i] < 0 || lanes[i] >= 32 {
          raise InvalidLaneIndex(lanes[i], 32)
        }
      }
      stack.pop(V128)
      stack.pop(V128)
      stack.push(V128)
    }
    I8x16Swizzle => {
      stack.pop(V128)
      stack.pop(V128)
      stack.push(V128)
    }

    // Splat (scalar -> vector)
    I8x16Splat | I16x8Splat | I32x4Splat => {
      stack.pop(I32)
      stack.push(V128)
    }
    I64x2Splat => {
      stack.pop(I64)
      stack.push(V128)
    }
    F32x4Splat => {
      stack.pop(F32)
      stack.push(V128)
    }
    F64x2Splat => {
      stack.pop(F64)
      stack.push(V128)
    }

    // Extract lane (vector -> scalar) with lane validation
    I8x16ExtractLaneS(lane) | I8x16ExtractLaneU(lane) => {
      if lane < 0 || lane >= 16 {
        raise InvalidLaneIndex(lane, 16)
      }
      stack.pop(V128)
      stack.push(I32)
    }
    I16x8ExtractLaneS(lane) | I16x8ExtractLaneU(lane) => {
      if lane < 0 || lane >= 8 {
        raise InvalidLaneIndex(lane, 8)
      }
      stack.pop(V128)
      stack.push(I32)
    }
    I32x4ExtractLane(lane) => {
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      stack.pop(V128)
      stack.push(I32)
    }
    I64x2ExtractLane(lane) => {
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      stack.pop(V128)
      stack.push(I64)
    }
    F32x4ExtractLane(lane) => {
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      stack.pop(V128)
      stack.push(F32)
    }
    F64x2ExtractLane(lane) => {
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      stack.pop(V128)
      stack.push(F64)
    }

    // Replace lane (vector, scalar -> vector) with lane validation
    I8x16ReplaceLane(lane) => {
      if lane < 0 || lane >= 16 {
        raise InvalidLaneIndex(lane, 16)
      }
      stack.pop(I32)
      stack.pop(V128)
      stack.push(V128)
    }
    I16x8ReplaceLane(lane) => {
      if lane < 0 || lane >= 8 {
        raise InvalidLaneIndex(lane, 8)
      }
      stack.pop(I32)
      stack.pop(V128)
      stack.push(V128)
    }
    I32x4ReplaceLane(lane) => {
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      stack.pop(I32)
      stack.pop(V128)
      stack.push(V128)
    }
    I64x2ReplaceLane(lane) => {
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      stack.pop(I64)
      stack.pop(V128)
      stack.push(V128)
    }
    F32x4ReplaceLane(lane) => {
      if lane < 0 || lane >= 4 {
        raise InvalidLaneIndex(lane, 4)
      }
      stack.pop(F32)
      stack.pop(V128)
      stack.push(V128)
    }
    F64x2ReplaceLane(lane) => {
      if lane < 0 || lane >= 2 {
        raise InvalidLaneIndex(lane, 2)
      }
      stack.pop(F64)
      stack.pop(V128)
      stack.push(V128)
    }

    // Comparison operations: v128 v128 -> v128
    I8x16Eq
    | I8x16Ne
    | I8x16LtS
    | I8x16LtU
    | I8x16GtS
    | I8x16GtU
    | I8x16LeS
    | I8x16LeU
    | I8x16GeS
    | I8x16GeU
    | I16x8Eq
    | I16x8Ne
    | I16x8LtS
    | I16x8LtU
    | I16x8GtS
    | I16x8GtU
    | I16x8LeS
    | I16x8LeU
    | I16x8GeS
    | I16x8GeU
    | I32x4Eq
    | I32x4Ne
    | I32x4LtS
    | I32x4LtU
    | I32x4GtS
    | I32x4GtU
    | I32x4LeS
    | I32x4LeU
    | I32x4GeS
    | I32x4GeU
    | I64x2Eq
    | I64x2Ne
    | I64x2LtS
    | I64x2GtS
    | I64x2LeS
    | I64x2GeS
    | F32x4Eq
    | F32x4Ne
    | F32x4Lt
    | F32x4Gt
    | F32x4Le
    | F32x4Ge
    | F64x2Eq
    | F64x2Ne
    | F64x2Lt
    | F64x2Gt
    | F64x2Le
    | F64x2Ge => {
      stack.pop(V128)
      stack.pop(V128)
      stack.push(V128)
    }

    // Unary v128 -> v128
    V128Not
    | I8x16Abs
    | I8x16Neg
    | I8x16Popcnt
    | I16x8Abs
    | I16x8Neg
    | I16x8ExtAddPairwiseI8x16S
    | I16x8ExtAddPairwiseI8x16U
    | I32x4Abs
    | I32x4Neg
    | I32x4ExtAddPairwiseI16x8S
    | I32x4ExtAddPairwiseI16x8U
    | I64x2Abs
    | I64x2Neg
    | F32x4Abs
    | F32x4Neg
    | F32x4Sqrt
    | F32x4Ceil
    | F32x4Floor
    | F32x4Trunc
    | F32x4Nearest
    | F64x2Abs
    | F64x2Neg
    | F64x2Sqrt
    | F64x2Ceil
    | F64x2Floor
    | F64x2Trunc
    | F64x2Nearest
    | I16x8ExtendLowI8x16S
    | I16x8ExtendHighI8x16S
    | I16x8ExtendLowI8x16U
    | I16x8ExtendHighI8x16U
    | I32x4ExtendLowI16x8S
    | I32x4ExtendHighI16x8S
    | I32x4ExtendLowI16x8U
    | I32x4ExtendHighI16x8U
    | I64x2ExtendLowI32x4S
    | I64x2ExtendHighI32x4S
    | I64x2ExtendLowI32x4U
    | I64x2ExtendHighI32x4U
    | I32x4TruncSatF32x4S
    | I32x4TruncSatF32x4U
    | F32x4ConvertI32x4S
    | F32x4ConvertI32x4U
    | I32x4TruncSatF64x2SZero
    | I32x4TruncSatF64x2UZero
    | F64x2ConvertLowI32x4S
    | F64x2ConvertLowI32x4U
    | F32x4DemoteF64x2Zero
    | F64x2PromoteLowF32x4
    // Relaxed SIMD: v128 -> v128
    | I32x4RelaxedTruncF32x4S
    | I32x4RelaxedTruncF32x4U
    | I32x4RelaxedTruncF64x2SZero
    | I32x4RelaxedTruncF64x2UZero => {
      stack.pop(V128)
      stack.push(V128)
    }

    // v128 -> i32 (boolean/bitmask)
    V128AnyTrue
    | I8x16AllTrue
    | I8x16Bitmask
    | I16x8AllTrue
    | I16x8Bitmask
    | I32x4AllTrue
    | I32x4Bitmask
    | I64x2AllTrue
    | I64x2Bitmask => {
      stack.pop(V128)
      stack.push(I32)
    }

    // Binary v128 v128 -> v128
    V128And
    | V128AndNot
    | V128Or
    | V128Xor
    | I8x16Add
    | I8x16AddSatS
    | I8x16AddSatU
    | I8x16Sub
    | I8x16SubSatS
    | I8x16SubSatU
    | I8x16MinS
    | I8x16MinU
    | I8x16MaxS
    | I8x16MaxU
    | I8x16AvgrU
    | I8x16NarrowI16x8S
    | I8x16NarrowI16x8U
    | I16x8Add
    | I16x8AddSatS
    | I16x8AddSatU
    | I16x8Sub
    | I16x8SubSatS
    | I16x8SubSatU
    | I16x8Mul
    | I16x8MinS
    | I16x8MinU
    | I16x8MaxS
    | I16x8MaxU
    | I16x8AvgrU
    | I16x8Q15MulrSatS
    | I16x8NarrowI32x4S
    | I16x8NarrowI32x4U
    | I16x8ExtMulLowI8x16S
    | I16x8ExtMulHighI8x16S
    | I16x8ExtMulLowI8x16U
    | I16x8ExtMulHighI8x16U
    | I32x4Add
    | I32x4Sub
    | I32x4Mul
    | I32x4MinS
    | I32x4MinU
    | I32x4MaxS
    | I32x4MaxU
    | I32x4DotI16x8S
    | I32x4ExtMulLowI16x8S
    | I32x4ExtMulHighI16x8S
    | I32x4ExtMulLowI16x8U
    | I32x4ExtMulHighI16x8U
    | I64x2Add
    | I64x2Sub
    | I64x2Mul
    | I64x2ExtMulLowI32x4S
    | I64x2ExtMulHighI32x4S
    | I64x2ExtMulLowI32x4U
    | I64x2ExtMulHighI32x4U
    | F32x4Add
    | F32x4Sub
    | F32x4Mul
    | F32x4Div
    | F32x4Min
    | F32x4Max
    | F32x4Pmin
    | F32x4Pmax
    | F64x2Add
    | F64x2Sub
    | F64x2Mul
    | F64x2Div
    | F64x2Min
    | F64x2Max
    | F64x2Pmin
    | F64x2Pmax
    // Relaxed SIMD: v128 v128 -> v128
    | I8x16RelaxedSwizzle
    | F32x4RelaxedMin
    | F32x4RelaxedMax
    | F64x2RelaxedMin
    | F64x2RelaxedMax
    | I16x8RelaxedQ15mulrS
    | I16x8RelaxedDotI8x16I7x16S => {
      stack.pop(V128)
      stack.pop(V128)
      stack.push(V128)
    }

    // Shift operations: v128 i32 -> v128
    I8x16Shl
    | I8x16ShrS
    | I8x16ShrU
    | I16x8Shl
    | I16x8ShrS
    | I16x8ShrU
    | I32x4Shl
    | I32x4ShrS
    | I32x4ShrU
    | I64x2Shl
    | I64x2ShrS
    | I64x2ShrU => {
      stack.pop(I32)
      stack.pop(V128)
      stack.push(V128)
    }

    // Bitselect: v128 v128 v128 -> v128
    V128Bitselect
    // Relaxed SIMD: v128 v128 v128 -> v128
    | F32x4RelaxedMadd
    | F32x4RelaxedNmadd
    | F64x2RelaxedMadd
    | F64x2RelaxedNmadd
    | I8x16RelaxedLaneselect
    | I16x8RelaxedLaneselect
    | I32x4RelaxedLaneselect
    | I64x2RelaxedLaneselect
    | I32x4RelaxedDotI8x16I7x16AddS => {
      stack.pop(V128)
      stack.pop(V128)
      stack.pop(V128)
      stack.push(V128)
    }
    _ =>
      raise UnsupportedInstruction(
        "non-SIMD instruction routed to SIMD validator: \{instr}",
      )
  }
}