Skip to content

Pattern matching causing redundant jumps in generated IL (maybe a bug, maybe a good reason?) #55334

@r30e

Description

@r30e

Version Used: cli 5.0.302

Steps to Reproduce:
Compile the following method (LangVersion=9.0) in Release mode

        public static string GetDescription(TestData data)
        {
            if (data is null or { A: null, B: null, C: null })
                return null;
            else
                return "Yay, stuff";
        }

Actual Behavior:

.method public hidebysig static string  GetDescription(class CoverletIssues.Program/TestData data) cil managed
{
  // Code size       44 (0x2c)
  .maxstack  1
  .locals init (bool V_0)
  IL_0000:  ldarg.0
  IL_0001:  brfalse.s  IL_001b
  IL_0003:  ldarg.0
  IL_0004:  callvirt   instance string CoverletIssues.Program/TestData::get_A()
  IL_0009:  brtrue.s   IL_001f
  IL_000b:  ldarg.0
  IL_000c:  callvirt   instance string CoverletIssues.Program/TestData::get_B()
  IL_0011:  brtrue.s   IL_001f
  IL_0013:  ldarg.0
  IL_0014:  callvirt   instance string CoverletIssues.Program/TestData::get_C()
  IL_0019:  brtrue.s   IL_001f
  IL_001b:  ldc.i4.1
  IL_001c:  stloc.0
  IL_001d:  br.s       IL_0021
  IL_001f:  ldc.i4.0
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  brfalse.s  IL_0026
  IL_0024:  ldnull
  IL_0025:  ret
  IL_0026:  ldstr      "Yay, stuff"
  IL_002b:  ret
} // end of method Program::GetDescription

Expected Behavior:

I was wondering if there is a reason for the 'branch, set, branch' pattern being used here?
The initial section contains four null checks each of which go to either IL_001b or IL_001f. These two locations set a local variable to 1 and 0 respectively and then branch based on this variable.

It seems to me that this is producing an unneceessary additional branch as the branches to IL_001b and IL_001f could, instead jump directly to IL_0024 and IL_0026 and produce the same result with one less conditional jump.

The reason I bring this up is that I think (unconfirmed) that this is affecting condition coverage metrics in Coverlet.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions