Skip to content

[naga] Undefined behaviour when casting floats to integer when out of range #7387

@jamienicol

Description

@jamienicol

Spinning this out from #7386 as it's more important we fix the UB promptly, whereas the exact result matching the spec is less important, especially as tint also appears to get that wrong.

Description
When casting an f32 to a i32 or u32 we may encounter undefined behaviour if the value is outwith the range of the target type.

  • back/hlsl does a constructor style cast
    • Spec says "The behavior is undefined if the truncated value cannot be represented in the destination type"
  • back/msl does a static_cast
    • Spec (Item 7.6) doesn't mention what happens when it's out of range. Presumably we therefore defer to C++14 section 4.9: "The behavior is undefined if the truncated value cannot be represented in the destination type"
  • back/spv emits OpConvertFToU/OpConvertFToS instructions.
    • Spec says: "Behavior is undefined if Result Type is not wide enough to hold the converted value."
  • back/glsl does a constructor style cast
    • I think it's safe to assume this is UB as well, but we don't care as much, for Firefox/webgpu at least.

Tint handles this with a wrapper function:

fn test(f: f32) -> i32 {
  return i32(f);
}

translates to... (metal, for example, but they do the same for all backends apart from wgsl)

int tint_f32_to_i32(float value) {
  return select(2147483647, select((-2147483647 - 1), int(value), (value >= -2147483648.0f)), (value <= 2147483520.0f));
}

int test(float f) {
  return tint_f32_to_i32(f);
}

Note they clamp to the range -2147483648..=2147483647, so still fall afoul of #7386, but avoid UB

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions