jax-ml/jax · error · ValueError

Can't bitcast {x.type} (of bitwidth {x_bw}) to {new_type} (o

Error message

Can't bitcast {x.type} (of bitwidth {x_bw}) to {new_type} (of bitwidth {new_bw})

What it means

bitcast only reinterprets bits, so the total bitwidth must be preserved. This check compares the source bitwidth (including whole vectors) to the target's; a size change cannot be a bitcast and must go through a cast/extension/truncation op instead.

Source

Thrown at jax/experimental/mosaic/gpu/utils.py:2066

    raise ValueError(f"Types must match, got {high.type} and {low.type}")
  if high.type != i32:
    high = bitcast(high, i32)
  if low.type != i32:
    low = bitcast(low, i32)
  if permutation.type != i32:
    permutation = bitcast(permutation, i32)
  result = llvm.inline_asm(
      i32, [high, low, permutation], "prmt.b32 $0, $1, $2, $3;", "=r,r,r,r"
  )
  assert isinstance(result, ir.Value)
  return bitcast(result, result_type)


def bitcast(x: ir.Value, new_type: ir.Type):
  if x.type == new_type:
    return x
  if (x_bw := bitwidth(x.type)) != (new_bw := bitwidth(new_type)):
    raise ValueError(
        f"Can't bitcast {x.type} (of bitwidth {x_bw}) to {new_type} (of"
        f" bitwidth {new_bw})"
    )
  if isinstance(x.type, ir.VectorType) and isinstance(new_type, ir.IntegerType):
    new_type = ir.IntegerType(new_type)
    x_ty = ir.VectorType(x.type)
    assert new_type.width == bitwidth(x_ty.element_type) * math.prod(x_ty.shape)
    return vector.extract(
        vector.bitcast(ir.VectorType.get((1,), new_type), x),
        dynamic_position=[],
        static_position=ir.DenseI64ArrayAttr.get([0]),
    )
  if isinstance(x.type, ir.IntegerType) and isinstance(new_type, ir.VectorType):
    new_type = ir.VectorType(new_type)
    x_ty = ir.IntegerType(x.type)
    assert x_ty.width == bitwidth(new_type.element_type) * math.prod(
        new_type.shape
    )

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Use a real cast: arith.trunci/extsi/extui for integers, arith.extf/truncf for floats
  2. If packing/unpacking vectors, make the total bitwidth match exactly (e.g. vector<2xbf16> <-> i32)
  3. For unequal widths, first cast to the same width then bitcast

Example fix

# before
x_i8 = bitcast(x_f32, ir.IntegerType.get_signless(8))  # 32 -> 8 bits
# after
x_i32 = bitcast(x_f32, ir.IntegerType.get_signless(32))
x_i8 = arith.trunci(ir.IntegerType.get_signless(8), x_i32)
Defensive patterns

Strategy: type-guard

Validate before calling

assert bitwidth(x.type) == bitwidth(new_type), 'bitcast must preserve total bitwidth'

Type guard

def can_bitcast(src_ty, dst_ty) -> bool:
    return bitwidth(src_ty) == bitwidth(dst_ty)

Prevention

When it happens

Trigger: Calling bitcast(x, new_type) where bitwidths differ, e.g. bitcasting vector<4xf32> (128 bits) to i64 (64 bits), or f32 to i8.

Common situations: Assuming bitcast can widen/narrow like reinterpret-with-resize; converting bf16x2 packing by bitcasting vector<2xbf16> to f32 (works) but to f16 (fails); mixing up truncation with reinterpreting.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/eb987ab276c17653. Report an issue: GitHub.