Skip to content

Commit c4abfe7

Browse files
committed
[naga wgsl] Let unary operators accept and produce abstract types.
Fixes #4445. Fixes #4492. Fixes #4435.
1 parent c4b4387 commit c4abfe7

11 files changed

+258
-187
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ This feature allowed you to call `global_id` on any wgpu opaque handle to get a
6868

6969
#### Naga
7070

71-
- Naga's WGSL front end now allows binary operators to produce values with abstract types, rather than concretizing thir operands. By @jimblandy in [#4850](https://github.com/gfx-rs/wgpu/pull/4850).
71+
- Naga's WGSL front end now allows operators to produce values with abstract types, rather than concretizing thir operands. By @jimblandy in [#4850](https://github.com/gfx-rs/wgpu/pull/4850) and [#4870](https://github.com/gfx-rs/wgpu/pull/4870).
7272

7373
- Naga's WGSL front and back ends now have experimental support for 64-bit floating-point literals: `1.0lf` denotes an `f64` value. There has been experimental support for an `f64` type for a while, but until now there was no syntax for writing literals with that type. As before, Naga module validation rejects `f64` values unless `naga::valid::Capabilities::FLOAT64` is requested. By @jimblandy in [#4747](https://github.com/gfx-rs/wgpu/pull/4747).
7474

naga/src/back/wgsl/writer.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,16 @@ impl<W: Write> Writer<W> {
10901090
Expression::Literal(literal) => match literal {
10911091
crate::Literal::F32(value) => write!(self.out, "{}f", value)?,
10921092
crate::Literal::U32(value) => write!(self.out, "{}u", value)?,
1093-
crate::Literal::I32(value) => write!(self.out, "{}i", value)?,
1093+
crate::Literal::I32(value) => {
1094+
// `-2147483648i` is not valid WGSL. The most negative `i32`
1095+
// value can only be expressed in WGSL using AbstractInt and
1096+
// a unary negation operator.
1097+
if value == i32::MIN {
1098+
write!(self.out, "i32(-2147483648)")?;
1099+
} else {
1100+
write!(self.out, "{}i", value)?;
1101+
}
1102+
}
10941103
crate::Literal::Bool(value) => write!(self.out, "{}", value)?,
10951104
crate::Literal::F64(value) => write!(self.out, "{:?}lf", value)?,
10961105
crate::Literal::I64(_) => {

naga/src/front/wgsl/lower/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,7 +1574,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
15741574
return Ok(Typed::Plain(handle));
15751575
}
15761576
ast::Expression::Unary { op, expr } => {
1577-
let expr = self.expression(expr, ctx)?;
1577+
let expr = self.expression_for_abstract(expr, ctx)?;
15781578
Typed::Plain(crate::Expression::Unary { op, expr })
15791579
}
15801580
ast::Expression::AddrOf(expr) => {

naga/src/front/wgsl/parse/lexer.rs

Lines changed: 42 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ fn consume_token(input: &str, generic: bool) -> (Token<'_>, &str) {
117117
let og_chars = chars.as_str();
118118
match chars.next() {
119119
Some('>') => (Token::Arrow, chars.as_str()),
120-
Some('0'..='9' | '.') => consume_number(input),
121120
Some('-') => (Token::DecrementOperation, chars.as_str()),
122121
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
123122
_ => (Token::Operation(cur), og_chars),
@@ -496,44 +495,60 @@ fn test_numbers() {
496495

497496
// MIN / MAX //
498497

499-
// min / max decimal signed integer
498+
// min / max decimal integer
500499
sub_test(
501-
"-2147483648i 2147483647i -2147483649i 2147483648i",
500+
"0i 2147483647i 2147483648i",
502501
&[
503-
Token::Number(Ok(Number::I32(i32::MIN))),
502+
Token::Number(Ok(Number::I32(0))),
504503
Token::Number(Ok(Number::I32(i32::MAX))),
505504
Token::Number(Err(NumberError::NotRepresentable)),
506-
Token::Number(Err(NumberError::NotRepresentable)),
507505
],
508506
);
509507
// min / max decimal unsigned integer
510508
sub_test(
511-
"0u 4294967295u -1u 4294967296u",
509+
"0u 4294967295u 4294967296u",
512510
&[
513511
Token::Number(Ok(Number::U32(u32::MIN))),
514512
Token::Number(Ok(Number::U32(u32::MAX))),
515513
Token::Number(Err(NumberError::NotRepresentable)),
516-
Token::Number(Err(NumberError::NotRepresentable)),
517514
],
518515
);
519516

520517
// min / max hexadecimal signed integer
521518
sub_test(
522-
"-0x80000000i 0x7FFFFFFFi -0x80000001i 0x80000000i",
519+
"0x0i 0x7FFFFFFFi 0x80000000i",
523520
&[
524-
Token::Number(Ok(Number::I32(i32::MIN))),
521+
Token::Number(Ok(Number::I32(0))),
525522
Token::Number(Ok(Number::I32(i32::MAX))),
526523
Token::Number(Err(NumberError::NotRepresentable)),
527-
Token::Number(Err(NumberError::NotRepresentable)),
528524
],
529525
);
530526
// min / max hexadecimal unsigned integer
531527
sub_test(
532-
"0x0u 0xFFFFFFFFu -0x1u 0x100000000u",
528+
"0x0u 0xFFFFFFFFu 0x100000000u",
533529
&[
534530
Token::Number(Ok(Number::U32(u32::MIN))),
535531
Token::Number(Ok(Number::U32(u32::MAX))),
536532
Token::Number(Err(NumberError::NotRepresentable)),
533+
],
534+
);
535+
536+
// min/max decimal abstract int
537+
sub_test(
538+
"0 9223372036854775807 9223372036854775808",
539+
&[
540+
Token::Number(Ok(Number::AbstractInt(0))),
541+
Token::Number(Ok(Number::AbstractInt(i64::MAX))),
542+
Token::Number(Err(NumberError::NotRepresentable)),
543+
],
544+
);
545+
546+
// min/max hexadecimal abstract int
547+
sub_test(
548+
"0 0x7fffffffffffffff 0x8000000000000000",
549+
&[
550+
Token::Number(Ok(Number::AbstractInt(0))),
551+
Token::Number(Ok(Number::AbstractInt(i64::MAX))),
537552
Token::Number(Err(NumberError::NotRepresentable)),
538553
],
539554
);
@@ -548,77 +563,43 @@ fn test_numbers() {
548563
const LARGEST_F32_LESS_THAN_ONE: f32 = 0.99999994;
549564
/// ≈ 1 + 2^−23
550565
const SMALLEST_F32_LARGER_THAN_ONE: f32 = 1.0000001;
551-
/// ≈ -(2^127 * (2 − 2^−23))
552-
const SMALLEST_NORMAL_F32: f32 = f32::MIN;
553566
/// ≈ 2^127 * (2 − 2^−23)
554567
const LARGEST_NORMAL_F32: f32 = f32::MAX;
555568

556569
// decimal floating point
557570
sub_test(
558-
"1e-45f 1.1754942e-38f 1.17549435e-38f 0.99999994f 1.0000001f -3.40282347e+38f 3.40282347e+38f",
571+
"1e-45f 1.1754942e-38f 1.17549435e-38f 0.99999994f 1.0000001f 3.40282347e+38f",
559572
&[
560-
Token::Number(Ok(Number::F32(
561-
SMALLEST_POSITIVE_SUBNORMAL_F32,
562-
))),
563-
Token::Number(Ok(Number::F32(
564-
LARGEST_SUBNORMAL_F32,
565-
))),
566-
Token::Number(Ok(Number::F32(
567-
SMALLEST_POSITIVE_NORMAL_F32,
568-
))),
569-
Token::Number(Ok(Number::F32(
570-
LARGEST_F32_LESS_THAN_ONE,
571-
))),
572-
Token::Number(Ok(Number::F32(
573-
SMALLEST_F32_LARGER_THAN_ONE,
574-
))),
575-
Token::Number(Ok(Number::F32(
576-
SMALLEST_NORMAL_F32,
577-
))),
578-
Token::Number(Ok(Number::F32(
579-
LARGEST_NORMAL_F32,
580-
))),
573+
Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_SUBNORMAL_F32))),
574+
Token::Number(Ok(Number::F32(LARGEST_SUBNORMAL_F32))),
575+
Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_NORMAL_F32))),
576+
Token::Number(Ok(Number::F32(LARGEST_F32_LESS_THAN_ONE))),
577+
Token::Number(Ok(Number::F32(SMALLEST_F32_LARGER_THAN_ONE))),
578+
Token::Number(Ok(Number::F32(LARGEST_NORMAL_F32))),
581579
],
582580
);
583581
sub_test(
584-
"-3.40282367e+38f 3.40282367e+38f",
582+
"3.40282367e+38f",
585583
&[
586-
Token::Number(Err(NumberError::NotRepresentable)), // ≈ -2^128
587584
Token::Number(Err(NumberError::NotRepresentable)), // ≈ 2^128
588585
],
589586
);
590587

591588
// hexadecimal floating point
592589
sub_test(
593-
"0x1p-149f 0x7FFFFFp-149f 0x1p-126f 0xFFFFFFp-24f 0x800001p-23f -0xFFFFFFp+104f 0xFFFFFFp+104f",
590+
"0x1p-149f 0x7FFFFFp-149f 0x1p-126f 0xFFFFFFp-24f 0x800001p-23f 0xFFFFFFp+104f",
594591
&[
595-
Token::Number(Ok(Number::F32(
596-
SMALLEST_POSITIVE_SUBNORMAL_F32,
597-
))),
598-
Token::Number(Ok(Number::F32(
599-
LARGEST_SUBNORMAL_F32,
600-
))),
601-
Token::Number(Ok(Number::F32(
602-
SMALLEST_POSITIVE_NORMAL_F32,
603-
))),
604-
Token::Number(Ok(Number::F32(
605-
LARGEST_F32_LESS_THAN_ONE,
606-
))),
607-
Token::Number(Ok(Number::F32(
608-
SMALLEST_F32_LARGER_THAN_ONE,
609-
))),
610-
Token::Number(Ok(Number::F32(
611-
SMALLEST_NORMAL_F32,
612-
))),
613-
Token::Number(Ok(Number::F32(
614-
LARGEST_NORMAL_F32,
615-
))),
592+
Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_SUBNORMAL_F32))),
593+
Token::Number(Ok(Number::F32(LARGEST_SUBNORMAL_F32))),
594+
Token::Number(Ok(Number::F32(SMALLEST_POSITIVE_NORMAL_F32))),
595+
Token::Number(Ok(Number::F32(LARGEST_F32_LESS_THAN_ONE))),
596+
Token::Number(Ok(Number::F32(SMALLEST_F32_LARGER_THAN_ONE))),
597+
Token::Number(Ok(Number::F32(LARGEST_NORMAL_F32))),
616598
],
617599
);
618600
sub_test(
619-
"-0x1p128f 0x1p128f 0x1.000001p0f",
601+
"0x1p128f 0x1.000001p0f",
620602
&[
621-
Token::Number(Err(NumberError::NotRepresentable)), // = -2^128
622603
Token::Number(Err(NumberError::NotRepresentable)), // = 2^128
623604
Token::Number(Err(NumberError::NotRepresentable)),
624605
],

naga/src/front/wgsl/parse/number.rs

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::borrow::Cow;
2-
31
use crate::front::wgsl::error::NumberError;
42
use crate::front::wgsl::parse::lexer::Token;
53

@@ -20,8 +18,6 @@ pub enum Number {
2018
F64(f64),
2119
}
2220

23-
// TODO: when implementing Creation-Time Expressions, remove the ability to match the minus sign
24-
2521
pub(in crate::front::wgsl) fn consume_number(input: &str) -> (Token<'_>, &str) {
2622
let (result, rest) = parse(input);
2723
(Token::Number(result), rest)
@@ -64,7 +60,9 @@ enum FloatKind {
6460
// | / 0[xX][0-9a-fA-F]+ [pP][+-]?[0-9]+ [fh]? /
6561

6662
// You could visualize the regex below via https://debuggex.com to get a rough idea what `parse` is doing
67-
// -?(?:0[xX](?:([0-9a-fA-F]+\.[0-9a-fA-F]*|[0-9a-fA-F]*\.[0-9a-fA-F]+)(?:([pP][+-]?[0-9]+)([fh]?))?|([0-9a-fA-F]+)([pP][+-]?[0-9]+)([fh]?)|([0-9a-fA-F]+)([iu]?))|((?:[0-9]+[eE][+-]?[0-9]+|(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?))([fh]?)|((?:[0-9]|[1-9][0-9]+))([iufh]?))
63+
// (?:0[xX](?:([0-9a-fA-F]+\.[0-9a-fA-F]*|[0-9a-fA-F]*\.[0-9a-fA-F]+)(?:([pP][+-]?[0-9]+)([fh]?))?|([0-9a-fA-F]+)([pP][+-]?[0-9]+)([fh]?)|([0-9a-fA-F]+)([iu]?))|((?:[0-9]+[eE][+-]?[0-9]+|(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?))([fh]?)|((?:[0-9]|[1-9][0-9]+))([iufh]?))
64+
65+
// Leading signs are handled as unary operators.
6866

6967
fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
7068
/// returns `true` and consumes `X` bytes from the given byte buffer
@@ -152,8 +150,6 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
152150

153151
let general_extract = ExtractSubStr::start(input, bytes);
154152

155-
let is_negative = consume!(bytes, b'-');
156-
157153
if consume!(bytes, b'0', b'x' | b'X') {
158154
let digits_extract = ExtractSubStr::start(input, bytes);
159155

@@ -216,10 +212,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
216212
} else {
217213
let kind = consume_map!(bytes, [b'i' => IntKind::I32, b'u' => IntKind::U32]);
218214

219-
(
220-
parse_hex_int(is_negative, digits, kind),
221-
rest_to_str!(bytes),
222-
)
215+
(parse_hex_int(digits, kind), rest_to_str!(bytes))
223216
}
224217
}
225218
} else {
@@ -272,7 +265,7 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
272265
return (Err(NumberError::Invalid), rest_to_str!(bytes));
273266
}
274267

275-
let digits_with_sign = general_extract.end(bytes);
268+
let digits = general_extract.end(bytes);
276269

277270
let kind = consume_map!(bytes, [
278271
b'i' => Kind::Int(IntKind::I32),
@@ -282,17 +275,14 @@ fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
282275
b'l', b'f' => Kind::Float(FloatKind::F64),
283276
]);
284277

285-
(
286-
parse_dec(is_negative, digits_with_sign, kind),
287-
rest_to_str!(bytes),
288-
)
278+
(parse_dec(digits, kind), rest_to_str!(bytes))
289279
}
290280
}
291281
}
292282
}
293283

294284
fn parse_hex_float_missing_exponent(
295-
// format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ )
285+
// format: 0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ )
296286
significand: &str,
297287
kind: Option<FloatKind>,
298288
) -> Result<Number, NumberError> {
@@ -301,7 +291,7 @@ fn parse_hex_float_missing_exponent(
301291
}
302292

303293
fn parse_hex_float_missing_period(
304-
// format: -?0[xX] [0-9a-fA-F]+
294+
// format: 0[xX] [0-9a-fA-F]+
305295
significand: &str,
306296
// format: [pP][+-]?[0-9]+
307297
exponent: &str,
@@ -312,29 +302,22 @@ fn parse_hex_float_missing_period(
312302
}
313303

314304
fn parse_hex_int(
315-
is_negative: bool,
316305
// format: [0-9a-fA-F]+
317306
digits: &str,
318307
kind: Option<IntKind>,
319308
) -> Result<Number, NumberError> {
320-
let digits_with_sign = if is_negative {
321-
Cow::Owned(format!("-{digits}"))
322-
} else {
323-
Cow::Borrowed(digits)
324-
};
325-
parse_int(&digits_with_sign, kind, 16, is_negative)
309+
parse_int(digits, kind, 16)
326310
}
327311

328312
fn parse_dec(
329-
is_negative: bool,
330-
// format: -? ( [0-9] | [1-9][0-9]+ )
331-
digits_with_sign: &str,
313+
// format: ( [0-9] | [1-9][0-9]+ )
314+
digits: &str,
332315
kind: Option<Kind>,
333316
) -> Result<Number, NumberError> {
334317
match kind {
335-
None => parse_int(digits_with_sign, None, 10, is_negative),
336-
Some(Kind::Int(kind)) => parse_int(digits_with_sign, Some(kind), 10, is_negative),
337-
Some(Kind::Float(kind)) => parse_dec_float(digits_with_sign, Some(kind)),
318+
None => parse_int(digits, None, 10),
319+
Some(Kind::Int(kind)) => parse_int(digits, Some(kind), 10),
320+
Some(Kind::Float(kind)) => parse_dec_float(digits, Some(kind)),
338321
}
339322
}
340323

@@ -363,7 +346,7 @@ fn parse_dec(
363346

364347
// Therefore we only check for overflow manually for decimal floating point literals
365348

366-
// input format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ ) [pP][+-]?[0-9]+
349+
// input format: 0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ ) [pP][+-]?[0-9]+
367350
fn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
368351
match kind {
369352
None => match hexf_parse::parse_hexf64(input, false) {
@@ -385,8 +368,8 @@ fn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, Numbe
385368
}
386369
}
387370

388-
// input format: -? ( [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) ([eE][+-]?[0-9]+)?
389-
// | -? [0-9]+ [eE][+-]?[0-9]+
371+
// input format: ( [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) ([eE][+-]?[0-9]+)?
372+
// | [0-9]+ [eE][+-]?[0-9]+
390373
fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
391374
match kind {
392375
None => {
@@ -411,12 +394,7 @@ fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, Numbe
411394
}
412395
}
413396

414-
fn parse_int(
415-
input: &str,
416-
kind: Option<IntKind>,
417-
radix: u32,
418-
is_negative: bool,
419-
) -> Result<Number, NumberError> {
397+
fn parse_int(input: &str, kind: Option<IntKind>, radix: u32) -> Result<Number, NumberError> {
420398
fn map_err(e: core::num::ParseIntError) -> NumberError {
421399
match *e.kind() {
422400
core::num::IntErrorKind::PosOverflow | core::num::IntErrorKind::NegOverflow => {
@@ -434,7 +412,6 @@ fn parse_int(
434412
Ok(num) => Ok(Number::I32(num)),
435413
Err(e) => Err(map_err(e)),
436414
},
437-
Some(IntKind::U32) if is_negative => Err(NumberError::NotRepresentable),
438415
Some(IntKind::U32) => match u32::from_str_radix(input, radix) {
439416
Ok(num) => Ok(Number::U32(num)),
440417
Err(e) => Err(map_err(e)),

naga/src/proc/constant_evaluator.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,8 @@ impl<'a> ConstantEvaluator<'a> {
11931193
UnaryOperator::Negate => match value {
11941194
Literal::I32(v) => Literal::I32(-v),
11951195
Literal::F32(v) => Literal::F32(-v),
1196+
Literal::AbstractInt(v) => Literal::AbstractInt(-v),
1197+
Literal::AbstractFloat(v) => Literal::AbstractFloat(-v),
11961198
_ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),
11971199
},
11981200
UnaryOperator::LogicalNot => match value {
@@ -1202,6 +1204,7 @@ impl<'a> ConstantEvaluator<'a> {
12021204
UnaryOperator::BitwiseNot => match value {
12031205
Literal::I32(v) => Literal::I32(!v),
12041206
Literal::U32(v) => Literal::U32(!v),
1207+
Literal::AbstractInt(v) => Literal::AbstractInt(!v),
12051208
_ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg),
12061209
},
12071210
}),

0 commit comments

Comments
 (0)