-
Notifications
You must be signed in to change notification settings - Fork 10
#30 - Coherent Pointers & Pointer Access Proposal #24
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
Conversation
proposals/030-coherent-pointers.md
Outdated
public enum PointerFlags | ||
{ | ||
None = 0, | ||
Coherent = 0b1 |
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.
Can be done in another proposal, but I expect PointerFlags
to be extended to also cover Const
.
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 would worry about overloading a single flags enum to handle too many different axes here. The following all seem, to me, to be reasonably orthogonal (and thus merit distinct generic arguments)
-
The type of the storage location being pointed to
-
The address space where the storage location resides
-
The access modes that are allowed to be performed through the pointer (read-only, write-only, or read-write)
-
The coherence scope for operations performed through the 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.
After asking Yong (separately from this PR), there was agreement on the design proposed.
proposals/030-coherent-pointers.md
Outdated
|
||
### Support The `globallycoherent` Keyword | ||
|
||
Slang will implement support for use of the `globallycoherent T*` keyword. This will be an alias to `Ptr<T, PointerFlags.Coherent, MemoryScope.Device>`. |
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 likely will need some type aliases e.g. CoherentPtr<T, MemoryScope>
so that the user does not need to specify the AddressSpace.UserPointer
in their Ptr
type signature.
Also need to handle the syntax of:
groupshared int * // a pointer to groupshared mem
int groupshared * // a same as above
coherent int groupshared* // a pointer to groupshared coherent mem
groupshared coherent int * // same as above
coherent groupshared int* // same as above
...
int * groupshared // a groupshared variable that is a global pointer
int * coherent groupshared * // a pointer to coherent groupshared mem
int * coherent * groupshared // a groupshared variable that is a pointer to coherent global mem
...
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 syntax is complex enough that we may want to completely disallow it in the initial implementation with an error on things like globallycoherent int*
, and then support it in a separate languauge proposal.
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.
CoherentPtr<T, MemoryScope>
worries me a bit since down the line there may be situations where a pointer abides to 2 different memory scopes for whatever reason SPIR-V requires.
Maybe the eventual solution here is named generic parameters: Ptr<type=T, flags=PointerFlags.Coherent, scope=MemoryScope.Device> ptr;
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 think this is important stuff to expose, but it also touches on a lot of big-picture topics in the language design that I wouldn't want us to bake too soon.
proposals/030-coherent-pointers.md
Outdated
public enum PointerFlags | ||
{ | ||
None = 0, | ||
Coherent = 0b1 |
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 would worry about overloading a single flags enum to handle too many different axes here. The following all seem, to me, to be reasonably orthogonal (and thus merit distinct generic arguments)
-
The type of the storage location being pointed to
-
The address space where the storage location resides
-
The access modes that are allowed to be performed through the pointer (read-only, write-only, or read-write)
-
The coherence scope for operations performed through the pointer.
proposals/030-coherent-pointers.md
Outdated
Coherent = 0b1 | ||
} | ||
|
||
__generic<T, uint64_t addrSpace=AddressSpace::UserPointer, PointerFlags flags=PointerFlags.None, MemoryScope coherentMemoryScope=MemoryScope.Device> |
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 would be nice to fold the PointerFlags.Coherent
part together with the coherentMemoryScope
part, because there's a strong relationship between the two:
-
It is meaningless to specify a scope without also specifying the
Coherent
flag, but this is allowed and means that code could look like it does something it doesn't. E.g., if they writePtr<T, PointerFlags.None, MemoryScope.WorkGroup>
that might look like there's something meaningful being communicated via theWorkGroup
argument, but there really isn't. -
A programmer really shouldn't be saying something is
Coherent
without taking a second to think about the scope of coherency they want.
I would propose that the simplest fix is to support a "scope" that represents operations only being guaranteed coherent within the current thread/work-item/lane/whatever-you-like-to-call-it.
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 will be implemented (per param, 1 unique enum) as per suggested in #24 (comment)
example (not final):
enum CoherentPtrFlags
{
NotCoherent,
CoherentToDevice,
CoherentToWorkgroup,
...
}
proposals/030-coherent-pointers.md
Outdated
groupshared T* // a pointer to groupshared memory: Ptr<T, AddressSpace::GroupShared> | ||
T groupshared* // a same as above | ||
|
||
coherent T groupshared* // a pointer to groupshared coherent memory: Ptr<T, AddressSpace::GroupShared, PointerFlags.Coherent, MemoryScope::WorkGroup> | ||
groupshared coherent T* // same as above | ||
coherent groupshared T* // same as above | ||
... | ||
int* groupshared // a groupshared variable that is a global pointer: groupshared Ptr<T, MemoryScope::Device> | ||
int* coherent groupshared* // a pointer to coherent groupshared memory: Ptr<T, AddressSpace::GroupShared, PointerFlags.Coherent, MemoryScope::WorkGroup> | ||
int* coherent* groupshared // a groupshared variable that is a pointer to coherent global memory: groupshared Ptr<T, PointerFlags.Coherent, MemoryScope::Device> | ||
|
||
globallycoherent T* // pointer which is coherent to global memory: groupshared Ptr<T, PointerFlags.Coherent, MemoryScope::Device> |
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 strongly object to a lot of this, just because it adds more modifiers and more keyword soup to Slang. We should be very cautious about adding tons of keywords and especially about keywords that can be used to qualify pointers...
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 personally have a conflicting opinion of these sorts of things:
- people moving from HLSL->Slang would want the soup of modifiers/keywords because it looks like HLSL
- This mess of modifiers/keywords is antagonistic to writing good generic code & is confusing.
- keywords promote not thinking about the non-keyword memory-scopes, this is the same problem we have with HLSL/GLSL/...
- keywords promote not using generics with
Ptr<>
types since the underlying type is obfuscated int* coherent groupshared*
!=int* coherent* groupshared
is bound to confuse people due to the small and nuanced differences in meaning.
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.
Final decision made: We are to remove this feature as a "next step". We should not have a stance on this currently.
proposals/030-coherent-pointers.md
Outdated
|
||
```c# | ||
RWStructuredBuffer<int> val; // Texture works as well. | ||
Ptr<int, PointerFlags.Coherent, MemoryScope.Subgroup> p = &val[0]; |
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 line of code already opens up a line of questions that I think is really important for this proposal to address: what are the conversion rules for Ptr
values under this proposal? Can a pointer with Coherent
be converted to one without? Vice-versa? Can one with a narrow coherency scope be converted to one with a wider scope? Why or why not?
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.
Thinking about it more, the big question I have is: what are the circumstances where a function would use a Ptr
type in its signature that has the Coherent
flag set, and in those circumstances what is the functions signature actually communicating?
If pointers with/without coherency and with different scopes are inter-convertible, then putting any of that information in a function signature is effectively meaningless to the code that wants to call it. It would be one of those things like marking a function parameter const
(not a pointer-to-const
, but just a const
parameter) where it is sitting there in the signature and thus gets exposed to callers, but in practice it is only significant to the implementer of a function, for the code they write in the function body.
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 think this gets into the bigger question on whether or not we should encode coherency in the pointer type itself.
The convenience of encoding it as part of the type is such that it propagates through pointer access chains, and the load/store site is automatically obeying to coherent access rules.
The downside is it complicates type coercion in a way that doesn't provide any actual meaning regarding caller/callee contract. I can't think of a case where a caller needs to know if a pointer is being accessed in a coherent way or not.
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 think allowing to freely cast is a give and take here (rather than just all bad as with const
).
- Users are likely to mix Coherent and Non-Coherent operations (expected behavior), hence casting freely will be useful. It is expected that a user will always need access to 2 variations of a
CoherentPtr
- This is a contrast to
const
since for allconst
variables, casting awayconst
is always a bad idea and almost always a source of bugs when passing around as parameters. - Coherence is much more comparable to atomic operations, "do we want the types to be atomic? Do we want per operation atomics?". Both approaches are used.
|
||
### Additional Pointer Arguments | ||
|
||
`Volatile` and `Const` are planned features for `Ptr`. |
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.
Why Volatile
when there is already coherent
?
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.
Volatile
is functionally different than Coherent
: "This access cannot be eliminated, duplicated, or combined with other accesses.".
Therefore Volatile
is a potential future addition (although I doubt high priority).
proposals/030-coherent-pointers.md
Outdated
|
||
```c# | ||
__generic<T, CoherentScope coherentScope> | ||
typealias CoherentPtr = Ptr<T, AddressSpace::UserPointer, coherentScope>; |
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 think this should be:
typealias CoherentPtr<T> = Ptr<T, AddressSpace::UserPointer, MemoryScope::Device>;
For anything else, the user should just use Ptr<>
type.
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 we should move the AddressSpace::...
param to the end of Ptr
given this change (although this would make the change a breaking-change)?
proposals/030-coherent-pointers.md
Outdated
|
||
### Support Casting Pointers With Different `CoherentScope` | ||
|
||
We will allow pointers with different `CoherentPtr` to be explicitly castable to each other. For example, `CoherentPtr<int, CoherentScope.Device>` will be castable to `CoherentPtr<int, MemoryScope.Workgroup>`. |
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.
... with different CoherentScope
to be explicitly...
proposals/030-coherent-pointers.md
Outdated
4. Support for coherent workgroup memory | ||
5. Support for coherent cooperative matrix & cooperative vector | ||
6. Support casting pointers with different coherent `MemoryScope` | ||
7. Support the `globallycoherent` keyword |
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.
Probably delete 7.
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 should make globallycoherent T*
an 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.
Looks good overall, some minor comments.
I think we want |
Perhaps we should just extend this proposal to also introduce |
proposals/030-coherent-pointers.md
Outdated
Subgroup = MemoryScope::Subgroup, | ||
Invocation = MemoryScope::Invocation, | ||
QueueFamily = MemoryScope::QueueFamily, | ||
ShaderCallKHR = MemoryScope::ShaderCallKHR, |
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.
Remove "KHR".
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.
removed 👍
Resolves: shader-slang/slang#7408
This proposal is for implementing (only for SPIR-V targets) coherent loads and stores through pointers. This proposal also implements ReadOnly (immutable) pointers & ReadWrite pointers.
A Slang playground example is provided here to test how
MakePointerVisibleKHR
and related operands function. This is not an implementation of the proposal.