Skip to content
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

JIT: Remove JTRUE(relop) invariant in the backend #82766

Merged
merged 9 commits into from Mar 7, 2023

Conversation

jakobbotsch
Copy link
Member

Today the backend (and much of the JIT) allows only JTRUE with a relop
operand, and it is always assumed that this relop appears right before
JTRUE in linear order. This change adds support for JTRUE nodes with
arbitrary integral operands from lowering and onwards. The operand is
also allowed to appear at arbitrary locations in LIR.

With this, we can now enable an optimization that turns
EQ/NE(relop/SETCC, 0) into just a (potentially) reversed condition. This
improves codegen for some cases with SELECTCC and gives us a simple way
to allow for compare chains that feed conditional branches in the
future.

Not so many diffs are expected, but they should come with #79283.

Currently based on top of #82610.

Example diffs:

[MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(int a, int b, int c)
{
    if (a < b & b < c & c < 10)
        return a * 42 + b;

    return 13;
}
         6B01001F          cmp     w0, w1
         7A42B024          ccmp    w1, w2, z, lt
         7A4AB844          ccmp    w2, #10, z, lt
-        9A9FA7E2          cset    x2, lt
-        340000A2          cbz     w2, G_M35561_IG05
-						;; size=20 bbWeight=1 PerfScore 3.00
-G_M35561_IG03:              ;; offset=001CH
+        540000AA          bge     G_M35561_IG05
+						;; size=16 bbWeight=1 PerfScore 2.50
+G_M35561_IG03:              ;; offset=0018H
         52800542          mov     w2, #42
         1B020400          madd    w0, w0, w2, w1
 						;; size=8 bbWeight=0.50 PerfScore 1.25
 
[MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(int a, int b, int c)
{
    if (a < b & b < c & c < 10)
        return 42;

    return 13;
}
G_M35561_IG01:              ;; offset=0000H
         910003FD          mov     fp, sp
 						;; size=8 bbWeight=1 PerfScore 1.50
 G_M35561_IG02:              ;; offset=0008H
+        528001A3          mov     w3, #13
+        52800544          mov     w4, #42
         6B01001F          cmp     w0, w1
         7A42B024          ccmp    w1, w2, z, lt
         7A4AB844          ccmp    w2, #10, z, lt
-        9A9FA7E0          cset    x0, lt
-        528001A1          mov     w1, #13
-        52800542          mov     w2, #42
-        7100001F          cmp     w0, #0
-        1A820020          csel    w0, w1, w2, eq
-						;; size=32 bbWeight=1 PerfScore 4.00
-G_M35561_IG03:              ;; offset=0028H
+        1A84A060          csel    w0, w3, w4, ge
+						;; size=24 bbWeight=1 PerfScore 3.00
+G_M35561_IG03:              ;; offset=0020H
         A8C17BFD          ldp     fp, lr, [sp], #0x10
         D65F03C0          ret     lr
 						;; size=8 bbWeight=1 PerfScore 2.00

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 28, 2023
@ghost ghost assigned jakobbotsch Feb 28, 2023
@ghost
Copy link

ghost commented Feb 28, 2023

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak
See info in area-owners.md if you want to be subscribed.

Issue Details

Today the backend (and much of the JIT) allows only JTRUE with a relop
operand, and it is always assumed that this relop appears right before
JTRUE in linear order. This change adds support for JTRUE nodes with
arbitrary integral operands from lowering and onwards. The operand is
also allowed to appear at arbitrary locations in LIR.

With this, we can now enable an optimization that turns
EQ/NE(relop/SETCC, 0) into just a (potentially) reversed condition. This
improves codegen for some cases with SELECTCC and gives us a simple way
to allow for compare chains that feed conditional branches in the
future.

Not so many diffs are expected, but they should come with #79283.

Currently based on top of #82610.

Example diffs:

[MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(int a, int b, int c)
{
    if (a < b & b < c & c < 10)
        return a * 42 + b;

    return 13;
}
         6B01001F          cmp     w0, w1
         7A42B024          ccmp    w1, w2, z, lt
         7A4AB844          ccmp    w2, #10, z, lt
-        9A9FA7E2          cset    x2, lt
-        340000A2          cbz     w2, G_M35561_IG05
-						;; size=20 bbWeight=1 PerfScore 3.00
-G_M35561_IG03:              ;; offset=001CH
+        540000AA          bge     G_M35561_IG05
+						;; size=16 bbWeight=1 PerfScore 2.50
+G_M35561_IG03:              ;; offset=0018H
         52800542          mov     w2, #42
         1B020400          madd    w0, w0, w2, w1
 						;; size=8 bbWeight=0.50 PerfScore 1.25
 
[MethodImpl(MethodImplOptions.NoInlining)]
public static int Foo(int a, int b, int c)
{
    if (a < b & b < c & c < 10)
        return 42;

    return 13;
}
G_M35561_IG01:              ;; offset=0000H
         910003FD          mov     fp, sp
 						;; size=8 bbWeight=1 PerfScore 1.50
 G_M35561_IG02:              ;; offset=0008H
+        528001A3          mov     w3, #13
+        52800544          mov     w4, #42
         6B01001F          cmp     w0, w1
         7A42B024          ccmp    w1, w2, z, lt
         7A4AB844          ccmp    w2, #10, z, lt
-        9A9FA7E0          cset    x0, lt
-        528001A1          mov     w1, #13
-        52800542          mov     w2, #42
-        7100001F          cmp     w0, #0
-        1A820020          csel    w0, w1, w2, eq
-						;; size=32 bbWeight=1 PerfScore 4.00
-G_M35561_IG03:              ;; offset=0028H
+        1A84A060          csel    w0, w3, w4, ge
+						;; size=24 bbWeight=1 PerfScore 3.00
+G_M35561_IG03:              ;; offset=0020H
         A8C17BFD          ldp     fp, lr, [sp], #0x10
         D65F03C0          ret     lr
 						;; size=8 bbWeight=1 PerfScore 2.00
Author: jakobbotsch
Assignees: jakobbotsch
Labels:

area-CodeGen-coreclr

Milestone: -

Today the backend (and much of the JIT) allows only JTRUE with a relop
operand, and it is always assumed that this relop appears right before
JTRUE in linear order. This change adds support for JTRUE nodes with
arbitrary integral operands from lowering and onwards. The operand is
also allowed to appear at arbitrary locations in LIR.

With this, we can now enable an optimization that turns
EQ/NE(relop/SETCC, 0) into just a (potentially) reversed condition. This
improves codegen for some cases with SELECTCC and gives us a simple way
to allow for compare chains that feed conditional branches in the
future.
@jakobbotsch
Copy link
Member Author

/azp run runtime-coreclr jitstress, runtime-coreclr libraries-jitstress, Fuzzlyn

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@jakobbotsch jakobbotsch marked this pull request as ready for review March 2, 2023 18:02
@jakobbotsch
Copy link
Member Author

cc @dotnet/jit-contrib PTAL @kunalspathak @BruceForstall @a74nh

Diffs.

With this PR #79283 should not need any (significant) backend work.

// Return Value:
// The next node to lower (usually nullptr).
//
GenTree* Lowering::LowerJTrue(GenTreeOp* jtrue)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've split out LA64 LowerJTrue logic since it is very different from the rest of the backends. It lowers the new JTRUE(op) nodes by turning them into JCMP(op, 0, NE). @shushanhf this is untested, can you please double check that it looks ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will check it for LA64.
Thanks.

src/coreclr/jit/lower.h Outdated Show resolved Hide resolved
use.ReplaceWith(op1);
BlockRange().Remove(cmp->gtGetOp2());
BlockRange().Remove(cmp);
return next;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difficulty I have with debugging the lowering phase, is that nodes can get renamed or moved, and there is no record in the JITDUMP except for the node dump before and after the phase. It's not always obvious what happened, especially when your work isn't focused on the lowering phase.
Not for this PR, but some extra debug throughout lowering would be useful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we should have more dump logging in lowering. I'll add some to this transform.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The additional dumping looks good. I think the "before" IR isn't required in these cases as that IR would have been printed the last time it was changed, but happy for it to stay like this.

@BruceForstall
Copy link
Member

I'm wondering (and this is unrelated to your change): in your examples, the C# code uses & not &&, which is not short-circuiting, but the conditional compare chain is short-circuiting. Does that only happen if the compare chain has no possible side-effects? What happens if the C# code uses && (which would be normal)? Do we get extra blocks that interfere with the optimization?

@jakobbotsch
Copy link
Member Author

jakobbotsch commented Mar 6, 2023

I'm wondering (and this is unrelated to your change): in your examples, the C# code uses & not &&, which is not short-circuiting, but the conditional compare chain is short-circuiting. Does that only happen if the compare chain has no possible side-effects? What happens if the C# code uses && (which would be normal)? Do we get extra blocks that interfere with the optimization?

Yes, with && we get extra blocks and don't do the optimization. @a74nh is working on turning short-circuiting && into bitwise & in #79283 which should expose tons of opportunities to if-conversion and to emitting ccmps in the backend.

@jakobbotsch
Copy link
Member Author

jakobbotsch commented Mar 6, 2023

@BruceForstall @a74nh I added some JITDUMP logging. I had to add a couple of new helpers to deal with the fact that there are no explicit flags edges, so there are new LIR::EarliestNode and LIR::Range::GetTreeRangeWithFlags functions.
@BruceForstall Can you take another look at the latest commit?

For the example above the jitdump is now:

*************** Starting PHASE Lowering nodeinfo
[000006] is a candidate for CCMP:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------                    t2 =LT        int    $100
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------                    t5 =LT        int    $101
                                                            ┌──▌  t2     int    
                                                            ├──▌  t5     int    
N007 ( 13,  7) [000006] -----------                    t6 =AND       int    $102

Conversion was legal. Result:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
N007 ( 13,  7) [000006] -----------                    t6 =    SETCC     int    cond=SLT

[000010] is a candidate for CCMP:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
N007 ( 13,  7) [000006] -----------                    t6 =    SETCC     int    cond=SLT
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------                    t9 =LT        int    $103
                                                            ┌──▌  t6     int    
                                                            ├──▌  t9     int    
N011 ( 20, 12) [000010] -----------                   t10 =AND       int    $104

Conversion was legal. Result:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------CCMP      void   cond=SLT flags=z
N011 ( 20, 12) [000010] -----------                   t10 =    SETCC     int    cond=SLT

@jakobbotsch
Copy link
Member Author

jakobbotsch commented Mar 6, 2023

I reworked the JITDUMP handling a bit more in the latest commit. LIR::Range::GetMarkedRange now has some best-effort handling for flags operands with a new markFlagsOperands template argument. Since it is best-effort there is a static assert that we do not pass it as true in release builds.
LIR::Range::GetTreeRangeWithFlags is debug-only and uses the new handling, and gtDispTreeRange then uses GetTreeRangeWithFlags. The benefit is that DISPTREERANGE now is flags-aware and prints a more useful range by default when flags are involved.
I then added some JITDUMP handling to LowerJTrue and LowerSelect too. Now the JITDUMP for the above example is (with some additional dumping at the end from LowerSelect):

[000006] is a candidate for CCMP:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------                    t2 =LT        int    $100
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------                    t5 =LT        int    $101
                                                            ┌──▌  t2     int    
                                                            ├──▌  t5     int    
N007 ( 13,  7) [000006] -----------                    t6 =AND       int    $102

Conversion was legal. Result:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
N007 ( 13,  7) [000006] -----------                    t6 =    SETCC     int    cond=SLT

[000010] is a candidate for CCMP:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
N007 ( 13,  7) [000006] -----------                    t6 =    SETCC     int    cond=SLT
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------                    t9 =LT        int    $103
                                                            ┌──▌  t6     int    
                                                            ├──▌  t9     int    
N011 ( 20, 12) [000010] -----------                   t10 =AND       int    $104

Conversion was legal. Result:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------CCMP      void   cond=SLT flags=z
N011 ( 20, 12) [000010] -----------                   t10 =    SETCC     int    cond=SLT

Lowering select:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------CCMP      void   cond=SLT flags=z
N011 ( 20, 12) [000010] -----------                   t10 =    SETCC     int    cond=SGE
N014 (  1,  2) [000014] -----------                   t14 =    CNS_INT   int    13 $45
N015 (  1,  2) [000016] -----------                   t16 =    CNS_INT   int    42 $46
                                                            ┌──▌  t10    int    
                                                            ├──▌  t14    int    
                                                            ├──▌  t16    int    
N016 ( 25, 20) [000018] -----------                   t18 =SELECT    int   

Converted to SELECTCC:
N001 (  1,  1) [000000] -----------                    t0 =    LCL_VAR   int    V00 arg0         u:1 (last use) $80
N002 (  1,  1) [000001] -----------                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
N004 (  1,  1) [000003] -----------                    t3 =    LCL_VAR   int    V01 arg1         u:1 (last use) $81
N005 (  1,  1) [000004] -----------                    t4 =    LCL_VAR   int    V02 arg2         u:1 $82
N008 (  1,  1) [000007] -----------                    t7 =    LCL_VAR   int    V02 arg2         u:1 (last use) $82
N009 (  1,  2) [000008] -c---------                    t8 =    CNS_INT   int    10 $44
N014 (  1,  2) [000014] -----------                   t14 =    CNS_INT   int    13 $45
N015 (  1,  2) [000016] -----------                   t16 =    CNS_INT   int    42 $46
                                                            ┌──▌  t0     int    
                                                            ├──▌  t1     int    
N003 (  6,  3) [000002] -----------CMP       void  
                                                            ┌──▌  t3     int    
                                                            ├──▌  t4     int    
N006 (  6,  3) [000005] -----------CCMP      void   cond=SLT flags=z
                                                            ┌──▌  t7     int    
                                                            ├──▌  t8     int    
N010 (  6,  4) [000009] -----------CCMP      void   cond=SLT flags=z
                                                            ┌──▌  t14    int    
                                                            ├──▌  t16    int    
N016 ( 25, 20) [000018] -----------                   t18 =SELECTCC  int    cond=SGE

Copy link
Member

@BruceForstall BruceForstall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!

Comment on lines +1167 to +1175
// markFlagsOperands - Whether the dataflow algorithm should also try to
// mark operands that are defining flags. For example,
// GT_JCC implicitly consumes the flags defined by the
// previous node in linear order. If this argument is
// true, then the algorithm will also include the flags
// definition (which is e.g. a CMP node) in the returned
// range.
// This works on a best-effort basis; the user should not
// rely on all flags defs to be found.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this down to "Template arguments"?

// range contains only nodes in the dataflow tree and false
// otherwise.
//
// Type arguments:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this "Template arguments:" instead?

Comment on lines 1176 to 1179
// root - The root of the dataflow tree.
// isClosed - An output parameter that is set to true if the returned
// range contains only nodes in the dataflow tree and false
// otherwise.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're here, can you fix this? root doesn't match the declaration. It's missing markCount and sideEffects.

@jakobbotsch jakobbotsch merged commit 8a28b11 into dotnet:main Mar 7, 2023
@jakobbotsch jakobbotsch deleted the lower-eqne-relop-0 branch March 7, 2023 00:01
@dotnet dotnet locked as resolved and limited conversation to collaborators Apr 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants