14

该属性System.Runtime.CompilerServices.MethodImplAttribute可用于向 JIT 编译器提供有关如何处理修饰方法的提示。特别是,该选项MethodImplOptions.AggressiveInlining指示编译器在可能的情况下内联受影响的方法。不幸的是,F# 编译器在生成 IL 时似乎只是忽略了这个属性。

示例:以下 C# 代码

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

被翻译成

.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
}

注意“aggressiveinlining”标志。

然而,这个 F# 代码

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

变成

.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
}

没有“积极的内联”。我还尝试将该属性应用于适当类的静态和非静态方法(type ...),但结果是相同的。

但是,如果我将它应用于自定义索引器,就像这样

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

得到的 IL 是

.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
}

...虽然我不确定这是否等同于 C# 编译器生成的“aggressiveinling”标志。

这种行为是期望/预期的吗?它是 F# 编译器中的错误吗?

(注意:我知道 F#inline关键字,但这仅适用于我的库的 F# 客户端,而不适用于 C# 消费者。)

4

1 回答 1

6

@kvb 是正确的,F#编译器似乎去掉了MethodImpl.

ComputeMethodImplAttribs调用IlxGen.fs中的方法来计算方法属性。

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

仔细查看行:4990:

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

第一个filter过滤掉MethodImplAttribute

现在,我在寻找原因,但这段代码可以追溯到latkin初始提交。我确实认为剥离我认为会影响 JIT 的MethodImpl特别是错误AggressiveInlining的:因此它需要在程序集中。

我建议注册一个问题。也许你至少可以得到一个解释。

于 2016-10-18T17:04:12.100 回答