-
Notifications
You must be signed in to change notification settings - Fork 353
WGSL: describe pointers #1368
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
WGSL: describe pointers #1368
Conversation
kvark
left a comment
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.
Thank you for writing this down, @dneto0 ! This is unblocking us to move forward with the ref/ptr discussion, and it's good timing that you made this now.
I'm worried that accepting the "load rule" described here would introduce ambiguity. For example, having a vector v of some type, and seeing v.x would no longer tell the reader what the type this expression is! It would depend on the context, in which it's expected. So this sounds quite like inference, but more complicated one that we wanted. And I think the complication is unnecessary, since we can just introduce a "&" to explicitly take a pointer of something, i.e. "v.x" would be a value type for "x" component, but "(&v).x" would be a pointer to "x".
| * By using the name of a variable that is in scope. | ||
| The result is the pointer to the storage allocated for the variable. | ||
| * By a <dfn noexport>composite value pointer sub-access</dfn> expression: | ||
| * Given a pointer to a vector, appending a single-letter vector access phrase |
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'm getting a bit confused by these rules. So the first rule says that when I type my_vector, where there is var my_vector:... in scope, the result of this is a pointer to vector. So, how can I then do const other: vec4<f32> = my_vector? The left side is a value, the right side is a pointer to a vector, according to these rules.
| * Given a pointer to a structure, appending a member access phrase | ||
| results in a pointer to the named member of the structure. | ||
| See [[#struct-access-expr]]. | ||
| * By passing a pointer value to a function, where the formal parameter of the function |
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.
What does "formal" mean in this context?
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.
We haven't written the section on functions yet.
I can add a TODO to define that.
| See [[#struct-access-expr]]. | ||
| * By passing a pointer value to a function, where the formal parameter of the function | ||
| has a matching pointer type. | ||
| The value denoted by the formal parameter inside the called function is the same |
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.
This is a bit unclear. Are we trying to say that the bit pattern representing these pointers is the same? Or something else?
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.
The pointer values are the same, i.e. they reference the same underlying storage. It's an implementation detail as to what the bit pattern of the pointers are.
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.
It reads as "the value of the parameter is the same as the pointer value", roughly, which is what confuses me.
Perhaps, it's the "value denoted by" semantics that I'm misreading?
|
|
||
| // An expression fragment using the name 'x' evaluates to a pointer of type | ||
| // ptr<private,f32>, pointing at the storage allocated for the variable 'x'. | ||
| const x_reference: ptr<private,f32> = x; |
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.
this is a bit confusing, since we are no longer being able to reason about an expression type of "x". So it make the rules more complex, and goes against limited inference of types that we want to get eventually.
was it considered to use &x syntax instead?
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.
When you add address-of, you now have to distinguish between l-value and r-value.
When I look at a variable in isolation, I don't know if it's being used as an l-value or r-value until further out in nesting. I have to defer evaluation (in my head) until I see or don't see a & operator used as address-of. Also & is the same name for a binary operator.
Example
&a.b.c.d[12];
In this example I would like to evaluate from what looks like the inside out (right to left) but I don't know if I'm dealing with pointers or values until I get to the very left. There's an inherent ambiguity there just the same.
C and C++ also have an arrow operator to step down into a pointer-to-composite: ->
In the current design you stay in the pointer domain for as long as possible, i.e. dereference (for load) only when necessary.
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.
When you add address-of, you now have to distinguish between l-value and r-value.
Right, and we have options there. I.e. we could even go with "store(my_pointer, value)", given that pointers are not widely used. This wouldn't affect variable assignments, obviously, since variables are not the pointers.
In this example I would like to evaluate from what looks like the inside out (right to left) but I don't know if I'm dealing with pointers or values until I get to the very left. There's an inherent ambiguity there just the same.
Is this really a problem to parse? Both C and Rust have "&" for the address.
I haven't implemented it, but it seems like the "&" is clear based on the parser context. if an expression is expected, then "&" is an address. If other things are expected, then "&" is a binary operator.
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.
@litherum was that a reaction to "store(my_pointer, value)" (which I don't think is really necessary), or to having "&" in general for taking a pointer?
| **Important:** | ||
| Pointer expressions often look the same as non-pointer expressions. | ||
| However, the [=load rule=] is only applied as a last resort: if it is possible | ||
| to interpret the expression so that it yields a pointer, then that is how the expression |
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 guess this load rule is what confuses me most. I believe this complexity is unnecessary, and pointer construction could be more explicit, without ruining the type inference.
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.
You pay either like this, or by not knowing if you have an l-value or an r-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.
I wonder if l-values are going to be a problem for us, really. Unlike C, we don't need the "=" to produce a result, we don't need to support "a = b = c", etc. So for WGSL, supporting syntax like "*pointer = value;" could be trivial:
- in the statement context, if it starts with "*", we are looking at the assignment (i.e. the l-value would follow).
Are there hidden pitfalls?
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.
Small update: I we are already tracking L-values versus R-values in WGSL frontend. I'd assume Tint does so, as well. I.e. when you parse "something.other = value", the parser needs to understand that "something.other" is an L-value. So de-referencing pointers with "*" doesn't introduce any more complexity in here than we already have.
| * By using the name of a variable that is in scope. | ||
| The result is the pointer to the storage allocated for the variable. | ||
| * By a <dfn noexport>composite value pointer sub-access</dfn> expression: | ||
| * Given a pointer to a vector, appending a single-letter vector access phrase |
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.
So, multi-letter swizzle would be a validation error?
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.
No. It's forced to be a dereference (load), followed by swizzle.
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.
How does that work with the pointer? I'm saying:
var a : vec3<f32>;
const b : ptr<function, vec2<f32>> = a.xz;
would be a validation error, yes? Or, are you saying that a.xz loads into a temporary and we take the address of the temporary?
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.
Yes, that's a validation error.
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.
It's a validation error because you can't form a pointer type from the right hand side.
You would like to use a rule from section "Component pointer from vector pointer" But that only has rules for a single letter after the dot.
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.
Right, I think we should add a brief 'multi-letter swizzles are a validation error' just to make that clear in the doc.
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.
By the way, in Metal, you can't take a pointer to a member of a vector, even if that member is a single field.
dneto0
left a comment
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've posted an update addressing most of the feedback from @dj2
| * By using the name of a variable that is in scope. | ||
| The result is the pointer to the storage allocated for the variable. | ||
| * By a <dfn noexport>composite value pointer sub-access</dfn> expression: | ||
| * Given a pointer to a vector, appending a single-letter vector access phrase |
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.
No. It's forced to be a dereference (load), followed by swizzle.
| * Given a pointer to a structure, appending a member access phrase | ||
| results in a pointer to the named member of the structure. | ||
| See [[#struct-access-expr]]. | ||
| * By passing a pointer value to a function, where the formal parameter of the function |
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.
We haven't written the section on functions yet.
I can add a TODO to define that.
| See [[#struct-access-expr]]. | ||
| * By passing a pointer value to a function, where the formal parameter of the function | ||
| has a matching pointer type. | ||
| The value denoted by the formal parameter inside the called function is the same |
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.
The pointer values are the same, i.e. they reference the same underlying storage. It's an implementation detail as to what the bit pattern of the pointers are.
| storage locations fully contained by the locations referenced by the | ||
| original pointer. | ||
| The originating variable of the result is the same as the | ||
| originating variable of the original pointer. |
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.
Yes, and more. Composite type covers structure, array, vector, matrix.
| **Important:** | ||
| Pointer expressions often look the same as non-pointer expressions. | ||
| However, the [=load rule=] is only applied as a last resort: if it is possible | ||
| to interpret the expression so that it yields a pointer, then that is how the expression |
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.
You pay either like this, or by not knowing if you have an l-value or an r-value.
|
This addresses #672 |
| #### Component pointer from vector pointer #### {#component-pointer-from-vector-pointer} | ||
|
|
||
| <table class='data'> | ||
| <caption>Getting a pointer to a component from a pointer to a vector</caption> |
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.
MSL doesn't support this.
Closest I could find in the MSL reference is:
• A pointer or reference to a vector with swizzles is an error:
float4 pos = float4(1.0f, 2.0f, 3.0f, 4.0f);
my_func(&pos.xy); // This is an illegal pointer to a swizzle.
But that's a multi-letter swizzle.
OpenCL C is more direct about the single-letter case:
It is illegal to take the address of a vector element and will result in a compilation error. For example:
float8 vf;
float *f = &vf.x; // is illegal
float2 *f2 = &vf.s07; // is illegal
|
The action item from the last meeting was to wait for @jdashg to make a counter proposal for explicit pointers. |
Also, variable and const identifer expressions
- Just say "pointer" instead of "pointer (or reference)" - remove typo "of of" - fix constant decl names in an example - outwith -> outside
This adds the missing type rule for pointer loading. Add more text re-emphasizing that the load only occurs as a last resort.
|
Rebased this against main, and resolved conflicts. |
|
@dneto0 is it intentional that this PR still has the implicit conversion of pointer into a value? |
- Add "memory view types" section - Defines reference type and pointer type - gives use cases and examples - Compares to other lanaguages - Adds: - composite reference sub-expressions - address-of expression (unary &) - indirection expression (unary *) - variable identifier expression - const identifier expression - formal parmaeter identifier expression TODO: grammar update for unary * and unary & Alternate to gpuweb#1368 Fixes: gpuweb#1456
- Add "memory view types" section - Defines reference type and pointer type - gives use cases and examples - Compares to other lanaguages - Adds: - composite reference sub-expressions - address-of expression (unary &) - indirection expression (unary *) - variable identifier expression - const identifier expression - formal parmaeter identifier expression TODO: grammar update for unary * and unary & Alternate to gpuweb#1368 Fixes: gpuweb#1456
- Add "memory view types" section - Defines reference type and pointer type - gives use cases and examples - Compares to other lanaguages - Adds: - composite reference sub-expressions - address-of expression (unary &) - indirection expression (unary *) - variable identifier expression - const identifier expression - formal parmaeter identifier expression TODO: grammar update for unary * and unary & Alternate to gpuweb#1368 Fixes: gpuweb#1456
- Add "memory view types" section - Defines reference type and pointer type - gives use cases and examples - Compares to other lanaguages - Adds: - composite reference sub-expressions - address-of expression (unary &) - indirection expression (unary *) - variable identifier expression - const identifier expression - formal parmaeter identifier expression TODO: grammar update for unary * and unary & Alternate to gpuweb#1368 Fixes: gpuweb#1456
* WGSL: describe ref ptr - Add "memory view types" section - Defines reference type and pointer type - gives use cases and examples - Compares to other lanaguages - Adds: - composite reference sub-expressions - address-of expression (unary &) - indirection expression (unary *) - variable identifier expression - const identifier expression - formal parmaeter identifier expression TODO: grammar update for unary * and unary & Alternate to #1368 Fixes: #1456 * Remove -> void from the newer examples * Apply review feedback Addressed feedback from @alan-baker and @ben-clayton Fixed typos, examples, links, and added an example showing what happens when the expression in a return statement is a reference. * Fix word * Fix types in comments in examples * Add grammar rule for unary &, unary *; address feedback - Added the grammar rule for address-of and indirection - Fixed algorithm name text (was copypasta) - Fixed example in the assignment-statement section. * Fix typo in the add_one example * Make address-of, indirection defined terms Improve cross-linking. * Add missing access qualifier in example * One more rename of "composite reference component expression" * Fix caption on structure-member reference expression * Convert ptr syntax examples to use a function declaration Applies feedback from @kvark review. Using a complete and valid function definition allows the example to be valid WGSL. - changed the pointer-to-uniform example to pointer-to-function, because pointer-to-uniform arguments won't be valid. (Sorry, that's a restriction from baseline SPIR-V in Vulkan restriction.) * Remove "Const identifier expression" That was pulled out to #1586 * For assignment statement, spell out memory *locations* * Add cross-reference to function-param expression
|
Superceded by #1569 as the new direction. |
Also, variable and const identifer expressions