-
Notifications
You must be signed in to change notification settings - Fork 1k
feat(naga): constant evaluation for select
#7602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(naga): constant evaluation for select
#7602
Conversation
cb645c6
to
3806939
Compare
3806939
to
27726d9
Compare
27726d9
to
7d5a24a
Compare
I think it would be good to have just one or two smoke tests for this, like the ones already there at the bottom of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could be mistaken on some of these, but I think there may be some simplifications possible.
|
||
let select_single_component = | ||
|this: &mut Self, reject_scalar, reject, accept, condition| { | ||
let accept = this.cast(accept, reject_scalar, span)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder, is this cast
really necessary at all? That is, in #7572, we will end up doing all the type checking and automatic conversions in the WGSL front end, at which point this code can assume that its operands are well-typed. Then it only needs to throw errors if it encounters something it can't make progress with (like, a condition that's not bool
or vecN<bool>
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All I know is that if I don't have it, the tests cases I'm using fail, even when consuming #7572. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concretely, this fails in constant evaluation if we don't cast
:
const_assert select(42f, 9001i, true) == 9001f;
My understanding is that we want to match the first argument's type, independent of condition
.
} | ||
}; | ||
|
||
match (&self.expressions[reject], &self.expressions[accept]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case where the condition is bool
and not vecN<bool>
, can't we just return reject
or accept
directly, without ever looking at its value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it not possible to reuse any of the component_wise
machinery for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RE: component_wise_*
: It wasn't possible previously because we're using a cast
operation before performing a component-wise operation here; we already take on the scope of delving into all components before knowing we have the right types on LHS and RHS.
I might have something wrong here, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least with my current arguments, this simplification depends on whether we can remove cast
at this conversation: #7602 (comment)
f03f9bd
to
e84c6f1
Compare
I played around with this a bit today and realized something that I hadn't really appreciated before, and which I think is part of why this has not been smooth (and why I've been giving not-so-great advice). The This undercuts some assumptions we've generally made in the WGSL front end and constant evaluator. Generally, if constant evaluation was able to take place at all, then that means the evaluator was able to find the values it needed to operate on, which in turn means that the call was well-typed. If the call is ill-typed, then constant evaluation fails, and we take the branch in According to this pattern, we would simply rewrite Thus, for If we decide to fully check it in the WGSL front end, here's what I came up with for "select" => {
let mut args = ctx.prepare_args(arguments, 3, span);
let mut values = [
self.expression_for_abstract(args.next()?, ctx)?,
self.expression_for_abstract(args.next()?, ctx)?,
];
let condition = self.expression(args.next()?, ctx)?;
for &value in &values {
ctx.grow_types(value)?;
}
let mut consensus_scalar = ctx
.automatic_conversion_consensus(&values)
.map_err(|_idx| Error::SelectArgumentsHaveNoCommonType {
select_call: span,
})?;
if !ctx.is_const(condition) {
consensus_scalar = consensus_scalar.concretize();
}
ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?;
let [reject, accept] = values;
args.finish()?;
ir::Expression::Select {
reject,
accept,
condition,
}
} This also handles The Mysterious Rule in the WGSL overload resolution algorithm, with the call to I also noticed that we had some really weird tests. fn select_pointers(which: bool) -> i32 {
var x: i32 = 1;
var y: i32 = 2;
let p = select(&x, &y, which);
return *p;
} There is no planet on which WGSL's |
e84c6f1
to
9cd034a
Compare
I'm going to combine this PR into #7572. There have been enough linked questions of behavior that I think it's better to test, review, and merge them as a unit. |
@jimblandy: I have incorporated your code, and modified it quite a bit so that diagnostics help the user better than the "weird" case you noted. 🙂 |
Connections
select
#7572.Testing
Squash or Rebase?
Squash unless multiple commits.
Checklist
CHANGELOG.md
entry.