-
Notifications
You must be signed in to change notification settings - Fork 779
No special case for unannotated params #5094
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
No special case for unannotated params #5094
Conversation
Initially the change here was forcing unannotated parameters to be treated as uses (`in` parameters). Instead this changes to not handle them explicitly. Most `in` parameter uses should produce `LValueToRValue` casts which will be identified as uses through other means, but by removing this special case we can support unannotated AST nodes from generated ASTs through this analysis based on their type and usage alone. Fixes microsoft#5093
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.
lg pending some editing in the comments and maybe another test case
RWByteAddressBuffer buffer; | ||
|
||
// No expected diagnostic here. InterlockedAdd is not annotated with HLSL | ||
// parameter annotations. |
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 comment is confusing. Isn't the reason there's no diagnostic because it is annotated with parameter annotations?
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.
Confusingly and probably incorrectly it isn’t annotated. My comment lacks sufficient details here. This change ignores unannotated parameters and instead leaves it up to the rules for C/C++, which ignores values used as pass-by-reference parameters.
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.
Yeah, that is confusing. I would think that the reason there's no diagnostic is that the third parameter is annotated with out
, like an ordinary case where no warning should be emitted (and where we should have a test for that as you pointed out above). Here's the intrinsic definition:
void [[]] InterlockedAdd64(in uint byteOffset, in u64 value, out any_int64 original) : interlockedadd_immediate;
Or is it the case that even though this has out
in the intrinsic definition, it doesn't add the HLSLOutAttr
to the parameter decl like an ordinary HLSL function would, yet we still need to test to ensure that it doesn't think this is a use of the uninitialized 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.
Yes, we don’t translate the annotations from the intrinsic definitions to the annotations in the AST, we just use them to generate the qual types for the arguments. InterlockedAdd
ends up with a signature something like InterlockedAdd(int, int, int && __restrict)
, with no HLSL annotations on the ParamVarDecl
s
I don’t think many of these are generating correct AST representations, but since they’re completely missing the HLSL annotations the best thing we can do is rely on C/C++ rules.
@@ -496,11 +496,10 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) { | |||
if (FD->getNumParams() > ParamIdx) { | |||
ParmVarDecl *PD = FD->getParamDecl(ParamIdx); | |||
bool HasIn = PD->hasAttr<HLSLInAttr>(); | |||
bool HasOut = PD->hasAttr<HLSLOutAttr>(); | |||
bool HasInOut = PD->hasAttr<HLSLInOutAttr>(); | |||
// If we have an in annotation or no annotation (implcit in), this is a | |||
// use not an initialization. |
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 this comment stale? I don't think it describes what's happening accurately even before your change.
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 in
added elsewhere by default for user function params?
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, this only covers explicit annotations. Pass-by-value parameters (including explicitly annotated in
) should automatically be treated as uses because they’ll pretty much always have an LValueToRValue
cast which is a use.
Thinking on it we need the in
check here is to cover the case of in out
vs inout
, but otherwise we wouldn’t need to check in
at all.
Many thanks, this solves the issue on our end! However, it also surfaces a bunch of issues on the following pattern (that I discarded yesterday by writing in #5093 that the bool GetOptionalThing(out float result) {
if (...) {
result = ...;
return true;
}
return false;
} with DXC obviously complaining that How should this pattern be implemented in HLSL? I worked around the warning by adding |
Glad to be helpful 😄
Now you're hitting the new warning exactly as intended.
This is the fun bit. The reason we introduced this issue is because people were misusing DXC in order to avoid redundant copies tries to optimize the code by passing in the address of the variable you want to store the end result to. We found cases where DXC was doing that unsafely, which I fixed back in January. Fixing those cases surfaced bugs in shaders where they were relying either on the unsafe optimization or outright undefined behavior in DXC. This code illustrates a (trivial) case where the optimization is unsafe (because the memory aliases), and the exposed undefined behavior made an invalid shader seem valid. These new warnings surface the undefined behavior to the user in a more actionable and identifiable way. Rather than a validation error, or a rendering glitch, we tell you exactly where the uninitialized value is propagating from. The important point here for
For an
If you expect the value to have a sensible result and to not overwrite it, you should make it an If you just want the warning to go away and are willing to get surprised along the way you have three options:
|
One last note worth sharing, these warning conditions are hard errors in FXC (see: FXC example). Because this behavior has been allowed in DXC forever, we decided to make it a default-enabled warning instead of an error. |
@llvm-beanz thanks for clarifying that in such a super-detailed manner 🤩! I don't want the warnings to go away at all but rather solve them correctly, less UB means less time spent debugging (we code in Rust for a reason, too). Sounds like I'll be determining on a case-by-case basis whether to use |
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!
In hindsight, is this intentional? I'd have a similar "uninitialized" error when using the |
Initially the change here was forcing unannotated parameters to be treated as uses (
in
parameters). Instead this changes to not handle them explicitly.Most
in
parameter uses should produceLValueToRValue
casts which will be identified as uses through other means, but by removing this special case we can support unannotated AST nodes from generated ASTs through this analysis based on their type and usage alone.Fixes #5093