Monday, August 05, 2019

F# under the covers XVII -- Code reuse loopiness

For some level of complexity and size, the F# compiler is happy to say "I've already done something like that, let's jump back and do it again!" putting an apparent loop into iteration-free code. It's not a common thing -- I've seen it exactly once the the FSharp.Core v4.7 library --

        match t1, t2 with
        | SetEmpty, t2  -> add comparer k t2 // drop t1 = empty 
        | t1, SetEmpty  -> add comparer k t1 // drop t2 = empty 
        | SetOne k1, t2 -> add comparer k (add comparer k1 t2)
        | t1, SetOne k2 -> add comparer k (add comparer k2 t1)
        | SetNode (k1, t11, t12, h1), SetNode (k2, t21, t22, h2) ->

The IL for this contains the expected forward conditional jumps from the match to the cases, but the second SetOne case preps the k2, t1 arguments then jumps backwards into the middle of the previous case, and exits after completing the shared call structure.

 IL_0029: ldloc.2
 IL_002a: isinst class Microsoft.FSharp.Collections.SetTree`1/SetOne
 IL_002f: brtrue.s IL_0055

 // item2 = t2;
 IL_0031: ldarg.3
 IL_0032: stloc.0
 // item = setOne.item;
 IL_0033: ldloc.1
 IL_0034: ldfld !0 class Microsoft.FSharp.Collections.SetTree`1/SetOne::item
 IL_0039: stloc.3

 // return add(comparer, k, add(comparer, item, item2));
 IL_003a: ldarg.0
 IL_003b: ldarg.2
 IL_003c: ldarg.0
 IL_003d: ldloc.3
 IL_003e: ldloc.0
 IL_003f: call class Microsoft.FSharp.Collections.SetTree`1 Microsoft.FSharp.Collections.SetTreeModule::'add'(class [mscorlib]System.Collections.Generic.IComparer`1, !!0, class Microsoft.FSharp.Collections.SetTree`1)
 IL_0044: call class Microsoft.FSharp.Collections.SetTree`1 Microsoft.FSharp.Collections.SetTreeModule::'add'(class [mscorlib]System.Collections.Generic.IComparer`1, !!0, class Microsoft.FSharp.Collections.SetTree`1)
 // (no C# code)
 IL_0049: ret

 // item2 = t1;
 IL_004a: ldarg.1
 IL_004b: stloc.0

 // return add(comparer, k, item2);
 IL_004c: ldarg.0
 IL_004d: ldarg.2
 IL_004e: ldloc.0
 IL_004f: call class Microsoft.FSharp.Collections.SetTree`1 Microsoft.FSharp.Collections.SetTreeModule::'add'(class [mscorlib]System.Collections.Generic.IComparer`1, !!0, class Microsoft.FSharp.Collections.SetTree`1)
 // (no C# code)
 IL_0054: ret

 // item2 = t2;
 IL_0055: ldarg.3
 // item = setOne.item;
 IL_0056: ldloc.1
 IL_0057: ldfld !0 class Microsoft.FSharp.Collections.SetTree`1/SetOne::item
 IL_005c: stloc.3
 // (no C# code)
 IL_005d: stloc.0
 IL_005e: br.s IL_003a

This provided an interesting exercise for the branch-chasing algorithm in AltCover (inspired by the one in OpenCover), which hadn't anticipated a backwards leap inside a sequence point, and went off into a spin until told to look for ret and similar termination points.

Thursday, August 01, 2019

July cycling

On the summer bike starting at 1480.8,and ending at 1659.8 plus 2 off-meter, for a total of 181.0 miles (YTD 1246). Up on last month, partly due to a longer bike-ride at the start of the month, north to Earith and around the edge of the fens; the very hot weather for a few days and then rain after knocked out a couple of days' business rides.


Earith town sign

This one pushed the limits to the north, just a little: