Skip to content

Commit 903f812

Browse files
committed
[naga msl-out] invalid code emitted for minimum i32/i64 value literal.
The literal `-2147483648` is parsed by Metal as negation of positive 2147483648. As 2147483648 is too large for a int, the expression is silently promoted to a long. Sometimes this does not matter as it will often be implicitly converted back to an int after the negation. However, if the expression is used in a bitcast then we hit a compiler error due to mismatched bitwidths. Similarily for `-9223372036854775808`, as 9223372036854775808 is too large for a long, metal emits a `-Wconstant-conversion` warning and changes the value to -9223372036854775808. This would then be negated again, possibly causing undefined behaviour. In both cases we can avoid the issue by expressing the literals as the second most negative value expressible by the type, minus one. eg `-2147483647 - 1` and `-9223372036854775807L - 1L`. We have added a test which uses the most negative i32 literal in an addition. Because we bitcast addition operands to unsigned in metal, this would cause a validation error without this fix. For the i64 case existing tests already make use of the minimum literal value. Passing the flag `-Werror=constant-conversion` to Metal during validation will therefore catch this issue.
1 parent c3121eb commit 903f812

11 files changed

+344
-291
lines changed

naga/src/back/msl/writer.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,13 +1494,31 @@ impl<W: Write> Writer<W> {
14941494
write!(self.out, "{value}u")?;
14951495
}
14961496
crate::Literal::I32(value) => {
1497-
write!(self.out, "{value}")?;
1497+
// `-2147483648` is parsed as unary negation of positive 2147483648.
1498+
// 2147483648 is too large for int32_t meaning the expression gets
1499+
// promoted to a int64_t which is not our intention. Avoid this by instead
1500+
// using `-2147483647 - 1`.
1501+
if value == i32::MIN {
1502+
write!(self.out, "({} - 1)", value + 1)?;
1503+
} else {
1504+
write!(self.out, "{value}")?;
1505+
}
14981506
}
14991507
crate::Literal::U64(value) => {
15001508
write!(self.out, "{value}uL")?;
15011509
}
15021510
crate::Literal::I64(value) => {
1503-
write!(self.out, "{value}L")?;
1511+
// `-9223372036854775808` is parsed as unary negation of positive
1512+
// 9223372036854775808. 9223372036854775808 is too large for int64_t
1513+
// causing Metal to emit a `-Wconstant-conversion` warning, and change the
1514+
// value to `-9223372036854775808`. Which would then be negated, possibly
1515+
// causing undefined behaviour. Avoid this by instead using
1516+
// `-9223372036854775808L - 1L`.
1517+
if value == i64::MIN {
1518+
write!(self.out, "({}L - 1L)", value + 1)?;
1519+
} else {
1520+
write!(self.out, "{value}L")?;
1521+
}
15041522
}
15051523
crate::Literal::Bool(value) => {
15061524
write!(self.out, "{value}")?;

naga/tests/in/wgsl/operators.wgsl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ fn arithmetic() {
160160
let mul_vector1 = vec3f(two_f) * mat4x3f();
161161

162162
let mul = mat4x3<f32>() * mat3x4<f32>();
163+
164+
// Arithmetic involving the minimum value i32 literal. What we're really testing here
165+
// is how this literal is expressed by Naga backends. eg in Metal, `-2147483648` is
166+
// silently promoted to a `long` which we don't want. The addition ensures this would
167+
// be caught as a compiler error, as we bitcast the operands to unsigned which fails
168+
// if the expression's type has an unexpected width.
169+
var prevent_const_eval: i32;
170+
var wgpu_7437 = prevent_const_eval + -2147483648;
163171
}
164172

165173
fn bit() {

naga/tests/out/glsl/wgsl-operators.main.Compute.glsl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ void logical() {
5959
}
6060

6161
void arithmetic() {
62+
int prevent_const_eval = 0;
63+
int wgpu_7437_ = 0;
6264
float neg0_1 = -(1.0);
6365
ivec2 neg1_1 = -(ivec2(1));
6466
vec2 neg2_ = -(vec2(1.0));
@@ -131,6 +133,8 @@ void arithmetic() {
131133
vec3 mul_vector0_ = (mat4x3(0.0) * vec4(1.0));
132134
vec4 mul_vector1_ = (vec3(2.0) * mat4x3(0.0));
133135
mat3x3 mul = (mat4x3(0.0) * mat3x4(0.0));
136+
int _e175 = prevent_const_eval;
137+
wgpu_7437_ = (_e175 + -2147483648);
134138
return;
135139
}
136140

naga/tests/out/hlsl/wgsl-operators.hlsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ float3x4 ZeroValuefloat3x4() {
121121

122122
void arithmetic()
123123
{
124+
int prevent_const_eval = (int)0;
125+
int wgpu_7437_ = (int)0;
126+
124127
float neg0_1 = -(1.0);
125128
int2 neg1_1 = naga_neg((int(1)).xx);
126129
float2 neg2_ = -((1.0).xx);
@@ -193,6 +196,8 @@ void arithmetic()
193196
float3 mul_vector0_ = mul((1.0).xxxx, ZeroValuefloat4x3());
194197
float4 mul_vector1_ = mul(ZeroValuefloat4x3(), (2.0).xxx);
195198
float3x3 mul_ = mul(ZeroValuefloat3x4(), ZeroValuefloat4x3());
199+
int _e175 = prevent_const_eval;
200+
wgpu_7437_ = asint(asuint(_e175) + asuint(int(-2147483648)));
196201
return;
197202
}
198203

naga/tests/out/msl/wgsl-abstract-types-operators.msl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ constant uint plus_u_uai_1 = 3u;
2626
constant uint plus_u_u_u_1 = 3u;
2727
constant uint bitflip_u_u = 0u;
2828
constant uint bitflip_uai = 0u;
29-
constant int least_i32_ = -2147483648;
29+
constant int least_i32_ = (-2147483647 - 1);
3030
constant float least_f32_ = -340282350000000000000000000000000000000.0;
3131
constant int shl_iaiai = 4;
3232
constant int shl_iai_u_1 = 4;
@@ -36,7 +36,7 @@ constant int shr_iaiai = 0;
3636
constant int shr_iai_u_1 = 0;
3737
constant uint shr_uaiai = 0u;
3838
constant uint shr_uai_u = 0u;
39-
constant int wgpu_4492_ = -2147483648;
39+
constant int wgpu_4492_ = (-2147483647 - 1);
4040

4141
void runtime_values(
4242
) {

naga/tests/out/msl/wgsl-conversion-float-to-int-no-f64.msl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ void test_const_eval(
1919
long max_f16_to_i64_ = 65504L;
2020
ulong min_f16_to_u64_ = 0uL;
2121
ulong max_f16_to_u64_ = 65504uL;
22-
int min_f32_to_i32_ = -2147483648;
22+
int min_f32_to_i32_ = (-2147483647 - 1);
2323
int max_f32_to_i32_ = 2147483520;
2424
uint min_f32_to_u32_ = 0u;
2525
uint max_f32_to_u32_ = 4294967040u;
26-
long min_f32_to_i64_ = -9223372036854775808L;
26+
long min_f32_to_i64_ = (-9223372036854775807L - 1L);
2727
long max_f32_to_i64_ = 9223371487098961920L;
2828
ulong min_f32_to_u64_ = 0uL;
2929
ulong max_f32_to_u64_ = 18446742974197923840uL;
30-
int min_abstract_float_to_i32_ = -2147483648;
30+
int min_abstract_float_to_i32_ = (-2147483647 - 1);
3131
int max_abstract_float_to_i32_ = 2147483647;
3232
uint min_abstract_float_to_u32_ = 0u;
3333
uint max_abstract_float_to_u32_ = 4294967295u;
34-
long min_abstract_float_to_i64_ = -9223372036854775808L;
34+
long min_abstract_float_to_i64_ = (-9223372036854775807L - 1L);
3535
long max_abstract_float_to_i64_ = 9223372036854774784L;
3636
ulong min_abstract_float_to_u64_ = 0uL;
3737
ulong max_abstract_float_to_u64_ = 18446744073709549568uL;

naga/tests/out/msl/wgsl-int64.msl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ long int64_function(
8484
long _e74 = val;
8585
val = as_type<long>(as_type<ulong>(_e74) + as_type<ulong>(as_type<metal::long4>(_e71).w));
8686
long _e77 = val;
87-
val = as_type<long>(as_type<ulong>(_e77) + as_type<ulong>(-9223372036854775808L));
87+
val = as_type<long>(as_type<ulong>(_e77) + as_type<ulong>((-9223372036854775807L - 1L)));
8888
long _e83 = input_uniform.val_i64_;
8989
long _e86 = input_storage.val_i64_;
9090
output.val_i64_ = as_type<long>(as_type<ulong>(_e83) + as_type<ulong>(_e86));

naga/tests/out/msl/wgsl-operators.msl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ metal::uint2 naga_mod(metal::uint2 lhs, metal::uint2 rhs) {
117117

118118
void arithmetic(
119119
) {
120+
int prevent_const_eval = {};
121+
int wgpu_7437_ = {};
120122
float neg0_1 = -(1.0);
121123
metal::int2 neg1_1 = naga_neg(metal::int2(1));
122124
metal::float2 neg2_ = -(metal::float2(1.0));
@@ -189,6 +191,8 @@ void arithmetic(
189191
metal::float3 mul_vector0_ = metal::float4x3 {} * metal::float4(1.0);
190192
metal::float4 mul_vector1_ = metal::float3(2.0) * metal::float4x3 {};
191193
metal::float3x3 mul = metal::float4x3 {} * metal::float3x4 {};
194+
int _e175 = prevent_const_eval;
195+
wgpu_7437_ = as_type<int>(as_type<uint>(_e175) + as_type<uint>((-2147483647 - 1)));
192196
return;
193197
}
194198

0 commit comments

Comments
 (0)