Skip to content

Commit 7129ad5

Browse files
LegNeatojamienicol
authored andcommitted
[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 e4106de commit 7129ad5

File tree

10 files changed

+340
-287
lines changed

10 files changed

+340
-287
lines changed

naga/src/back/msl/writer.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -1499,13 +1499,31 @@ impl<W: Write> Writer<W> {
14991499
write!(self.out, "{value}u")?;
15001500
}
15011501
crate::Literal::I32(value) => {
1502-
write!(self.out, "{value}")?;
1502+
// `-2147483648` is parsed as unary negation of positive 2147483648.
1503+
// 2147483648 is too large for int32_t meaning the expression gets
1504+
// promoted to a int64_t which is not our intention. Avoid this by
1505+
// instead using `-2147483647 - 1`.
1506+
if value == i32::MIN {
1507+
write!(self.out, "({} - 1)", value + 1)?;
1508+
} else {
1509+
write!(self.out, "{value}")?;
1510+
}
15031511
}
15041512
crate::Literal::U64(value) => {
15051513
write!(self.out, "{value}uL")?;
15061514
}
15071515
crate::Literal::I64(value) => {
1508-
write!(self.out, "{value}L")?;
1516+
// `-9223372036854775808` is parsed as unary negation of positive
1517+
// 9223372036854775808. 9223372036854775808 is too large for int64_t
1518+
// causing Metal to emit a `-Wconstant-conversion` warning, and change
1519+
// the value to `-9223372036854775808`. Which would then be negated,
1520+
// possibly causing undefined behaviour. Avoid this by instead using
1521+
// `-9223372036854775808L - 1L`.
1522+
if value == i64::MIN {
1523+
write!(self.out, "({}L - 1L)", value + 1)?;
1524+
} else {
1525+
write!(self.out, "{value}L")?;
1526+
}
15091527
}
15101528
crate::Literal::Bool(value) => {
15111529
write!(self.out, "{value}")?;

naga/tests/in/wgsl/operators.wgsl

+8
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/operators.main.Compute.glsl

+4
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/operators.hlsl

+5
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/abstract-types-operators.msl

+2-2
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/int64.msl

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ long int64_function(
8080
long _e74 = val;
8181
val = as_type<long>(as_type<ulong>(_e74) + as_type<ulong>(as_type<metal::long4>(_e71).w));
8282
long _e77 = val;
83-
val = as_type<long>(as_type<ulong>(_e77) + as_type<ulong>(-9223372036854775808L));
83+
val = as_type<long>(as_type<ulong>(_e77) + as_type<ulong>((-9223372036854775807L - 1L)));
8484
long _e83 = input_uniform.val_i64_;
8585
long _e86 = input_storage.val_i64_;
8686
output.val_i64_ = as_type<long>(as_type<ulong>(_e83) + as_type<ulong>(_e86));

naga/tests/out/msl/operators.msl

+4
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)