Fixed: Loop optimization is less than VB

I have the following code:

Function StringBuilderModification(s As String) As String
    Dim b = New System.Text.StringBuilder(10000)
    For i = 0 To s.Length - 1 Step 5
        b.Append(s, i, 5)
        b.Append("X")
    Next
    Return b.ToString
End Function

When I call this with a string of 10.000 chars long, it runs for 4741 ticks on VB and for 23471 ticks on Mercury. So Mercury takes 5 times longer somehow.

The IL generated by VB:

.method public static string StringBuilderModification (
        string s
    ) cil managed 
{
    .locals init (
        [0] class [mscorlib]System.Text.StringBuilder b,
        [1] int32 V_1,
        [2] int32 i
    )

    IL_0000: ldc.i4 10000
    IL_0005: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(int32)
    IL_000a: stloc.0
    IL_000b: ldarg.0
    IL_000c: callvirt instance int32 [mscorlib]System.String::get_Length()
    IL_0011: ldc.i4.1
    IL_0012: sub.ovf
    IL_0013: stloc.1
    IL_0014: ldc.i4.0
    IL_0015: stloc.2
    IL_0016: br.s IL_0032
    .loop
    {
        IL_0018: ldloc.0
        IL_0019: ldarg.0
        IL_001a: ldloc.2
        IL_001b: ldc.i4.5
        IL_001c: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string,  int32,  int32)
        IL_0021: pop
        IL_0022: ldloc.0
        IL_0023: ldstr "X"
        IL_0028: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
        IL_002d: pop
        IL_002e: ldloc.2
        IL_002f: ldc.i4.5
        IL_0030: add.ovf
        IL_0031: stloc.2

        IL_0032: ldloc.2
        IL_0033: ldloc.1
        IL_0034: ble.s IL_0018
    }

    IL_0036: ldloc.0
    IL_0037: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString()
    IL_003c: ret
}

IL generated by Mercury:
.method public hidebysig static string StringBuilderModification (
string s
) cil managed
{
.locals init (
[0] class [mscorlib]System.Text.StringBuilder V_0,
[1] int32 V_1
)

    IL_0000: ldc.i4 10000
    IL_0005: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(int32)
    IL_000a: stloc.0
    IL_000b: ldc.i4.0
    IL_000c: stloc.1
    IL_000d: br.s IL_0029
    .loop
    {
        IL_000f: ldloc.0
        IL_0010: ldarg.0
        IL_0011: ldloc.1
        IL_0012: ldc.i4.5
        IL_0013: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string,  int32,  int32)
        IL_0018: pop
        IL_0019: ldloc.0
        IL_001a: ldstr "X"
        IL_001f: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
        IL_0024: pop
        IL_0025: ldloc.1
        IL_0026: ldc.i4.5
        IL_0027: add
        IL_0028: stloc.1

        IL_0029: ldloc.1
        IL_002a: ldarg.0
        IL_002b: callvirt instance int32 [mscorlib]System.String::get_Length()
        IL_0030: ldc.i4.1
        IL_0031: sub
        IL_0032: ble.s IL_000f
    }

    IL_0034: ldloc.0
    IL_0035: callvirt instance string [mscorlib]System.Text.StringBuilder::ToString()
    IL_003a: ret
}

When I look at the decompiled VB code, I see an optimization that is not done in Mercury:
VB:
image
Mercury:

What means that s.Length - 1 is calculated in every loop again for the Mercury code, while it is only done once with the VB code.

This recalculation that is done in Mercury is indeed - as Mohammed Hamdy pointed out - a breaking change; VB only calculates the loop once and does not change it when the loop variable changes.

Logged as bugs://E25494.

Agreed, that’s wrong. A for loop should determine the end expression once, opposed to a while loop

bugs://E25494 was closed as fixed.