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
Cast to ushort is dropped in release #10435
Comments
cc @dotnet/jit-contrib |
I took a closer look at this and this caused by This is correct with respect to the parent XOR node that can be narrowed from Ironically, the same
|
This one is quite interesting: // Generated by Fuzzlyn on 2018-06-11 20:55:08
// Seed: 6332377919370969218
// Reduced from 80.2 KiB to 0.6 KiB
// Debug: Outputs 65487
// Release: Outputs -49
struct S0
{
public byte F0;
public sbyte F1;
public sbyte F2;
public S0(byte f0, sbyte f1, sbyte f2)
{
F0 = f0;
F1 = f1;
F2 = f2;
}
}
public class Program
{
static bool[][] s_9 = new bool[][]{new bool[]{true}};
public static void Main()
{
S0 vr45 = new S0(0, 0, 0);
M5(vr45);
}
static bool M5(S0 arg1)
{
arg1 = new S0(0, -50, 0);
char var0 = (char)(1U ^ arg1.F1);
System.Console.WriteLine((int)var0);
return s_9[0][0];
}
} In 32-bit and 64-bit .NET Core, the results are as described by the top comments. However in 32-bit .NET Framework (using JIT32), the results are the opposite: In debug, the program prints -49 and in release, the program prints 65487 (the correct result). Should I open a separate issue for this? I am unsure where to report JIT32 bugs. |
Yep, the same
Developer Community I'd guess - https://developercommunity.visualstudio.com/spaces/61/index.html |
This can cause problems in debug too: public class Program
{
static int s_1 = 0xff;
public static void Main()
{
int vr14 = (ushort)(sbyte)s_1;
System.Console.WriteLine(vr14);
}
} This program prints -1 in both debug and release, even though
to
then the inner cast is narrowed and removed (correct):
However, the outer-cast has no way to know that it should not narrow this tree further, because of the comma-node (which has type
So as far as I can tell, the culprit is this: |
That might be but it's not the only problem with narrow tree, at least because you can reproduce the bug without a COMMA node. It's somewhat difficult to pinpoint the actual problem because it's not really clear what I wrote a new one (originally with the intention to address some CQ issues) but it's still pretty much WIP. And it's probably better if a more surgical fix is found for this particular issue. Though I'm a bit skeptical that such a fix exists, one or two I tried resulted in unrelated CQ regressions.
JIT's IR tends to follow IL model (and that one follows typical high level language models) where small integer operations (add, sub, mul etc.) do not exist. Supporting small integer ops would likely add significant complexity and have questionable benefit.
Sure, because normally there's no such thing as a small int typed operand. The few nodes that do use small int types (most commonly indirs) actually produce int values by implicit widening. |
One way to think about narrow types is that when optimizing we'd prefer that they appear only at the boundaries of trees -- widening at leaves (~loads) and narrowing at the root (~stores). As @mikedn says this more or less mirrors IL and machine semantics where narrow types are a storage concept (args, locals, fields), not an operation concept. When there are casts in the middle of a tree the general idea is to migrate them either up to the root or down to the leaves of the tree (or sometimes both, in complex trees with multiple casts), as this can often lead to simplifications. But as should be clear, this is something that needs to be done carefully... |
I see. Thanks to both of you for the explanations, that helps. So to sum up: we would (almost?) never want to actually relabel internal nodes to smaller types? |
Yeah, almost. There are some places where the JIT manages to use small types in less usual circumstances. 2 or 3 cases I happen to know about:
Yep. These casts are fun, you can classify them as narrowing or widening depending on how you look at them. |
I have surgical fixes for these three cases. I verified no diffs on x64 crossgen frameworks. I will verify diffs on ({x64, x86} x {crossgen, pmi} x {frameworks + tests}) and will create a PR next week. |
@jakobbotsch I can repro and have fixes for your first and third examples above. However, I can't repro the second example (that uses struct S0). I get the correct result on core in debug and release x64. Can you double check that you get -49 in release? |
I think I must have made a mistake in my comment previously. That example only reproduces with 32-bit JIT. I cannot reproduce it with 64-bit JIT. EDIT: To be clear it reproduces with RyuJIT for 32-bit code. With JIT32 (on full .NET framework), the behavior is flipped: in debug it prints the wrong result, and in release the correct result. |
Thank you for clarification. I reproduced that example with x86 RyuJIT and verified that my fixes cover it. |
No problem. Once you open the PR I can try to look at the rest of my examples and see if I can find some other corner cases not covered. |
The fix under NARROW_IND prevents transformation of, e.g., CAST int <- ushort <- int CLS_VAR byte into CLS_VAR byte. With the fix the CAST is not removed. The fix under GT_CAST prevents transformation of, e.g., CAST int <- ushort <- long CAST long <- int expr short into expt short. With the fix it gets transformed into CAST int <- ushort <- int expr short Fixes #18238. No diffs in frameworks and tests (pmi and crossgen, x64 and x86), except for the added test cases.
The fix under NARROW_IND prevents transformation of, e.g., CAST int <- ushort <- int CLS_VAR byte into CLS_VAR byte. With the fix the CAST is not removed. The fix under GT_CAST prevents transformation of, e.g., CAST int <- ushort <- long CAST long <- int expr short into expt short. With the fix it gets transformed into CAST int <- ushort <- int expr short Block cast optimizations in fgMorphCast if the cast exression is an active CSE candidate. Update cast exression value numbers when a cast is removed. Fixes #18238, #18850. No diffs in frameworks and tests (pmi and crossgen, x64 and x86), except for the added test cases.
The fix under NARROW_IND prevents transformation of, e.g., CAST int <- ushort <- int CLS_VAR byte into CLS_VAR byte. With the fix the CAST is not removed. The fix under GT_CAST prevents transformation of, e.g., CAST int <- ushort <- long CAST long <- int expr short into expt short. With the fix it gets transformed into CAST int <- ushort <- int expr short Block cast optimizations in fgMorphCast if the cast expression is an active CSE candidate. Update cast expression value numbers when a cast is removed. Fixes #18238, #18850. No diffs in frameworks and tests (pmi and crossgen, x64 and x86), except for the added test cases.
The fix under NARROW_IND prevents transformation of, e.g., CAST int <- ushort <- int CLS_VAR byte into CLS_VAR byte. With the fix the CAST is not removed. The fix under GT_CAST prevents transformation of, e.g., CAST int <- ushort <- long CAST long <- int expr short into expt short. With the fix it gets transformed into CAST int <- ushort <- int expr short Block cast optimizations in fgMorphCast if the cast expression is an active CSE candidate. Update cast expression value numbers when a cast is removed. Fixes #18238, #18850. No diffs in frameworks and tests (pmi and crossgen, x64 and x86), except for the added test cases.
The fix under NARROW_IND prevents transformation of, e.g., CAST int <- ushort <- int CLS_VAR byte into CLS_VAR byte. With the fix the CAST is not removed. The fix under GT_CAST prevents transformation of, e.g., CAST int <- ushort <- long CAST long <- int expr short into expt short. With the fix it gets transformed into CAST int <- ushort <- int expr short Block cast optimizations in fgMorphCast if the cast expression is an active CSE candidate. Update cast expression value numbers when a cast is removed. Fixes #18238, #18850. No diffs in frameworks and tests (pmi and crossgen, x64 and x86), except for the added test cases.
The following program gives different outputs for release and debug:
In release, the result is
4294966297
. In debug, the result is64537
.@mikedn has analyzed the cause here.
This issue repros on .NET framework as well with 64-bit JIT (it does not repro with 32-bit JIT).
The text was updated successfully, but these errors were encountered: