Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying MethodImplOptions.AggressiveInlining to F# functions

The attribute System.Runtime.CompilerServices.MethodImplAttribute can be used to give hints to the JIT compiler about how to handle the decorated method. In particular, the option MethodImplOptions.AggressiveInlining instructs the compiler to inline the affected method if possible. Unfortunately the F# compiler seems to simply ignore this attribute when generating IL.

Example: The following C# code

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Inc(int x) => x + 1;

is translated to

.method public hidebysig static int32  Inc(int32 x) cil managed aggressiveinlining
{     
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.1
    IL_0002:  add
    IL_0003:  ret
}

Note the "aggressiveinlining" flag.

This F# code however

[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
let inc x = x + 1

becomes

.method public static int32  inc(int32 x) cil managed
{
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldc.i4.1
    IL_0003:  add
    IL_0004:  ret
}

No "aggressiveinlining". I also tried to apply the attribute to static and non-static methods of proper classes (type ...), but the result is the same.

If however I apply it to a custom indexer, like so

type Dummy =
    member self.Item
        with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get x = x + 1

the resulting IL is

.method public hidebysig specialname instance int32 get_Item(int32 x) cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.MethodImplAttribute::.ctor(valuetype [mscorlib]System.Runtime.CompilerServices.MethodImplOptions) = ( 01 00 00 01 00 00 00 00 ) 
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  ldc.i4.1
    IL_0003:  add
    IL_0004:  ret
}

... though I'm not sure whether that is equivalent to the "aggressiveinling" flag generated by the C# compiler.

Is that behavior desired/expected? Is it a bug in the F# compiler?

(Note: I'm aware of the F# inline keyword, but that only works for F# clients of my library, not C# consumers.)

like image 904
Frank Avatar asked Oct 18 '16 09:10

Frank


1 Answers

@kvb is correct that it seems the F# compiler seems to strip out the MethodImpl.

ComputeMethodImplAttribs in IlxGen.fs is called to compute the method attributes.

and ComputeMethodImplAttribs cenv (_v:Val) attrs =
    let implflags = 
        match TryFindFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute attrs with
        | Some (Attrib(_,_,[ AttribInt32Arg flags ],_,_,_,_))  -> flags
        | _ -> 0x0

    let hasPreserveSigAttr = 
        match TryFindFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute attrs with
        | Some _ -> true
        | _ -> false

    // strip the MethodImpl pseudo-custom attribute    
    // The following method implementation flags are used here
    // 0x80 - hasPreserveSigImplFlag
    // 0x20 - synchronize
    // (See ECMA 335, Partition II, section 23.1.11 - Flags for methods [MethodImplAttributes]) 
    let attrs = attrs 
                    |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) 
                        |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
    let hasPreserveSigImplFlag = ((implflags &&& 0x80) <> 0x0) || hasPreserveSigAttr
    let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0
    let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0
    hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, attrs

Looking more closely around row: 4990:

    let attrs = attrs 
                    |> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not) 
                        |> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)

The first filter filters away MethodImplAttribute.

Now, I was looking a bit trying to find the rationale but this code stems back to latkin initial commit. I do think it's probably wrong to strip away the MethodImpl especially for AggressiveInlining which I believe affects the JIT:ing therefore it needs to be in the assembly.

I would recommend registering an issue. Perhaps you can get an explaination at least.

like image 55
Just another metaprogrammer Avatar answered Oct 27 '22 19:10

Just another metaprogrammer