You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Suboptimal codegen when comparing sbyte and 16-bit values #10337
The Foo method checks to see if the high bit is clear, and the Bar method checks to see if the byte has input pattern 10xxxxxx. These are both common operations when performing UTF-8 text processing. In the examples above, the Bar method has suboptimal codegen. I would instead expect the codegen to be the following.
@mikedn don't know if you looked yet -- from the dump it looks like morph is the culprit:
fgMorphTree BB01, stmt 1 (before)
[000005] ------------ * RETURN int
[000003] ------------ | /--* CNS_INT int -64
[000004] ------------ \--* LT int
[000002] ------------ \--* CAST int <- byte <- int
[000001] ------------ \--* LCL_VAR ubyte V00 arg0
fgMorphTree BB01, stmt 1 (after)
[000005] -----+------ * RETURN int
[000003] -----+------ | /--* CNS_INT int -64
[000004] -----+------ \--* LT int
[000002] -----+------ \--* CAST int <- byte <- int
[000007] -----+------ \--* CAST int <- ubyte <- int
[000001] -----+------ \--* LCL_VAR int V00 arg0
I tried following the rather tortuous logic in fgMorphCast to see where morph be going off the rails, but got lost in there and haven't found my way out.
Yes, there's morph code that attempts to get rid of some cast via optNarrowTree. I've yet to understand how exactly optNarrowTree is supposed to work though, parameter names like srct and dstt do not help... I'd guess it's mostly intended to deal with LONG -> INT narrowing and fails for small int types.
Lowering is also involved in this, the fact that we end up generating cmp cl,80h in some cases is the result of Lowering::OptimizeConstCompare. That one only handles casts to UBYTE/BOOL currently but I don't see any obstacle in extending it to support BYTE as well. But morph needs to be fixed first otherwise changing lowering has no effect.
00007ffe`2cb01630 80f980 cmpcl,80h00007ffe`2cb01633 0f92c0 setb al
could be
84C9 testcl,cl 0F9CC0 setge al
that is one byte shorter. Another one for lowering, it's kind of amazing how many such optimizations are possible around compares. OptimizeConstCompare already does quite a bit but there are still various things that are missing.
(The movzx is unnecessary, as the following instruction could just read cmp ax, 2000h. Or are 16-bit comparisons inefficient compared to 32-bit comparisons?)
The movzx is unnecessary, as the following instruction could just read cmp ax, 2000h. Or are 16-bit comparisons inefficient compared to 32-bit comparisons?
Yep, 16 bit instructions can be problematic - they require a special byte prefix (66h) that makes the instruction larger and can slow down instruction decoding, at least on Intel CPUs.
Though it looks like this particular example can be converted to something else:
Another pattern I'm seeing in some of the UTF-8 processing:
Sometimes I want to know if a byte is an ASCII character but also not a C0 control code. Basically, I want to test that it's in the range [ 0x20 .. 0x7F ]. (For my purposes, I don't consider 0x7F a C0 control character.)
The naive way to do this is with a standard single-branch range check:
This requires two registers instead of one and spends unnecessary time in the decoder. An idealized version would be as follows, which consumes only one register and spends minimal time in the decoder.
Activity
mikedn commentedon May 16, 2018
Taking a look, the JIT surely has a problem with useless casts...
AndyAyersMS commentedon May 17, 2018
@mikedn don't know if you looked yet -- from the dump it looks like morph is the culprit:
I tried following the rather tortuous logic in
fgMorphCastto see where morph be going off the rails, but got lost in there and haven't found my way out.mikedn commentedon May 17, 2018
Yes, there's morph code that attempts to get rid of some cast via
optNarrowTree. I've yet to understand how exactlyoptNarrowTreeis supposed to work though, parameter names likesrctanddsttdo not help... I'd guess it's mostly intended to deal withLONG->INTnarrowing and fails for small int types.Lowering is also involved in this, the fact that we end up generating
cmp cl,80hin some cases is the result ofLowering::OptimizeConstCompare. That one only handles casts toUBYTE/BOOLcurrently but I don't see any obstacle in extending it to supportBYTEas well. But morph needs to be fixed first otherwise changing lowering has no effect.mikedn commentedon May 17, 2018
And
could be
that is one byte shorter. Another one for lowering, it's kind of amazing how many such optimizations are possible around compares.
OptimizeConstComparealready does quite a bit but there are still various things that are missing.GrabYourPitchforks commentedon May 18, 2018
As lower priority, there's also opportunity for improvement for 16-bit comparisons, as below.
(The
movzxis unnecessary, as the following instruction could just readcmp ax, 2000h. Or are 16-bit comparisons inefficient compared to 32-bit comparisons?)mikedn commentedon May 18, 2018
Yep, 16 bit instructions can be problematic - they require a special byte prefix (66h) that makes the instruction larger and can slow down instruction decoding, at least on Intel CPUs.
Though it looks like this particular example can be converted to something else:
GrabYourPitchforks commentedon Aug 16, 2018
Another pattern I'm seeing in some of the UTF-8 processing:
Sometimes I want to know if a byte is an ASCII character but also not a C0 control code. Basically, I want to test that it's in the range
[ 0x20 .. 0x7F ]. (For my purposes, I don't consider 0x7F a C0 control character.)The naive way to do this is with a standard single-branch range check:
This requires two registers instead of one and spends unnecessary time in the decoder. An idealized version would be as follows, which consumes only one register and spends minimal time in the decoder.
[-]Suboptimal codegen when comparing sbyte values[/-][+]Suboptimal codegen when comparing sbyte and 16-bit values[/+]JIT: narrow small-typed compare with in-range constant on xarch
JIT: extend OptimizeConstCompare to remove TYP_BYTE casts (#129361)
JIT: extend OptimizeConstCompare to remove TYP_BYTE casts (#129361)
JIT: extend OptimizeConstCompare to remove TYP_BYTE casts (dotnet#129361