-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[RISC-V][JIT] Fix NaN canonicalization issue #88510
Merged
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -1556,7 +1556,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN | |||
const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); | ||||
const unsigned retRegCount = retTypeDesc->GetReturnRegCount(); | ||||
#else // !FEATURE_MULTIREG_RET | ||||
const unsigned retRegCount = 1; | ||||
const unsigned retRegCount = 1; | ||||
#endif // !FEATURE_MULTIREG_RET | ||||
|
||||
structPassingKind howToReturnStruct; | ||||
|
@@ -3794,7 +3794,17 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||
if (op1->IsIntegralConst()) | ||||
{ | ||||
int32_t i32Cns = (int32_t)op1->AsIntConCommon()->IconValue(); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
retNode = gtNewDconNode(*reinterpret_cast<float*>(&i32Cns), TYP_FLOAT); | ||||
#ifdef TARGET_RISCV64 | ||||
float f32Cns = *reinterpret_cast<float*>(&i32Cns); | ||||
if (FloatingPointUtils::isNaN(f32Cns)) | ||||
{ | ||||
retNode = gtNewDconNode(*reinterpret_cast<double*>(&i32Cns), TYP_FLOAT); | ||||
} | ||||
else | ||||
#endif // TARGET_RISCV64 | ||||
{ | ||||
retNode = gtNewDconNode(*reinterpret_cast<float*>(&i32Cns), TYP_FLOAT); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
|
@@ -3834,8 +3844,17 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, | |||
|
||||
if (op1->IsCnsFltOrDbl()) | ||||
{ | ||||
float f32Cns = (float)op1->AsDblCon()->DconValue(); | ||||
retNode = gtNewIconNode(*reinterpret_cast<int32_t*>(&f32Cns)); | ||||
#ifdef TARGET_RISCV64 | ||||
double f64Cns = op1->AsDblCon()->DconValue(); | ||||
float f32Cns = *reinterpret_cast<float*>(&f64Cns); | ||||
if (!FloatingPointUtils::isNaN(f32Cns)) | ||||
{ | ||||
f32Cns = (float)op1->AsDblCon()->DconValue(); | ||||
} | ||||
#else | ||||
float f32Cns = (float)op1->AsDblCon()->DconValue(); | ||||
#endif // TARGET_RISCV64 | ||||
retNode = gtNewIconNode(*reinterpret_cast<int32_t*>(&f32Cns)); | ||||
} | ||||
else | ||||
{ | ||||
|
@@ -4141,7 +4160,16 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, | |||
else | ||||
{ | ||||
assert(fromType == TYP_FLOAT); | ||||
float f32Cns = static_cast<float>(op1->AsDblCon()->DconValue()); | ||||
#ifdef TARGET_RISCV64 | ||||
double f64Cns = op1->AsDblCon()->DconValue(); | ||||
float f32Cns = *reinterpret_cast<float*>(&f64Cns); | ||||
if (!FloatingPointUtils::isNaN(f32Cns)) | ||||
{ | ||||
f32Cns = static_cast<float>(op1->AsDblCon()->DconValue()); | ||||
} | ||||
#else | ||||
float f32Cns = static_cast<float>(op1->AsDblCon()->DconValue()); | ||||
#endif // TARGET_RISCV64 | ||||
return gtNewIconNode(static_cast<int32_t>(BitOperations::SingleToUInt32Bits(f32Cns))); | ||||
} | ||||
} | ||||
|
@@ -4181,7 +4209,17 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, | |||
assert(toType == TYP_FLOAT); | ||||
|
||||
uint32_t u32Cns = static_cast<uint32_t>(op1->AsIntConCommon()->IconValue()); | ||||
return gtNewDconNode(BitOperations::UInt32BitsToSingle(u32Cns), TYP_FLOAT); | ||||
#ifdef TARGET_RISCV64 | ||||
float f32Cns = BitOperations::UInt32BitsToSingle(u32Cns); | ||||
if (FloatingPointUtils::isNaN(f32Cns)) | ||||
{ | ||||
return gtNewDconNode(*reinterpret_cast<double*>(&f32Cns), TYP_FLOAT); | ||||
} | ||||
else | ||||
#endif // TARGET_RISCV64 | ||||
{ | ||||
return gtNewDconNode(BitOperations::UInt32BitsToSingle(u32Cns), TYP_FLOAT); | ||||
} | ||||
} | ||||
} | ||||
else | ||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 is this change necessary? _isnan should not care about payload bits.
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.
Sure. I will make a new helper. Thank you. I wanted to know proper location and name. :)
Because when a float value is converted to
Double
with preserving payload and saved inDconValue
, the savedDouble
value is not 'NaN' inDouble
type.For example,
c
can be assumed asf64Cns
in the change.0.000000 nan 0.000000 0 1 0
Only the
*reinterpret_cast<float*>(&c)
isNaN
in the example.If I misunderstand your comment, please let me know.
Thank you!
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.
Conversion to a double by reinterpreting a 32 bit int is not correct. The right way to do it is something that constructs the uint64_t bits directly by ORing in the desired payload bits in the right place and returns
UInt64BitsToDouble
from that. It should mimic what our other hosts do for float -> double conversions. We should not be trying to store a 32 bit float in the 64 bit double field.I currently do not have access to a PC, but @tannergooding may be able to help with the exact right way to do the conversion that retains the payload bits on the proper way.
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 PR updates codes to use
fsd/flw
(preserving payload) orfcvt
(not preserving payload) in case of float isNaN
or not.I will wait for the exact solution. Thank you so much!!!
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 there are a few possible options. For the JIT side, all of them have the issue that the general codebase would need to be "audited" to ensure that all float->double handling is done correctly. We're not consistent about this today.
Update GenTreeDblCon to be more like GenTreeIcon
You can update
GenTreeDblCon
to have a union ofgtDconVal
andgtFconVal
. It would then exposefloat FconValue()
andvoid SetFconValue(float value)
and assert thatDcon
is only called forTYP_DOUBLE
andFcon
is only called for `TYP_FLOATThis will ensure that the values aren't ever accessed incorrectly and avoids the upcast/downcast issue entirely for the JIT. It does not resolve the issue for managed code and will likely have some minor TP hit due to needing to add branching to various callsites in the JIT.
Expose a payload preserving conversion helper
Just as it says, define a helper API that does a payload preserving conversion. This would be a software based conversion and would simply ensure the significand bits are copied up and truncated down, just as happens for other platforms.
This helper would be used by both the JIT and managed and ensures managed code also gets the right handling. This would incur a branch check per conversion on RISC-V.
Explicitly utilize
FMV.n.X
/FMV.X.n
These instructions allow storing
n-bits
in the largerf
register, preserving the payload. The value stored is aNaN boxed value
.One could utilize this to more efficiently store the data in
gtDconVal
, but it has many of the same issues/considerations where it needs to be done everywhere and consistently; otherwise subtle bugs could creep in.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'd suggest to introduce the payload preserving conversion helper for now and use it in the cases found in this PR. I think that's going to be the least invasive fix. We can consider the more complete/invasive fixes as part of .NET 9.
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.
@jakobbotsch @tannergooding Thank you so much for the comments.
I made a conversion helper and updated all. However, I don't know what is the right way to convert, so I just retained the conversion method as it is. Could you please give the right way what you mentioned?
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.
Could you take a look? Thank you so much.
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.
@clamp03 The conversion functions should look something like:
You should change the JIT to use this conversion function in the places you have identified.
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.
Awesome!!! I didn't think so. Thank you so much.