【问题标题】:Why basic interface in type parameters causes resulting monomorphic function to use runtime.assertI2I()?为什么类型参数中的基本接口会导致生成的单态函数使用 runtime.assertI2I()?
【发布时间】:2022-07-03 04:00:34
【问题描述】:

多态函数(完整代码见here):

type Intf interface{ 
    Do() 
}
type Intf2 interface {
    Intf
    Do2()
}

func CallIntf[T Intf](intf T) {
    intf.Do()
}

手动单形:

func CallIntf_mono(intf Intf) {
    intf.Do()
}

实例化:

    var intf2 Intf2
    intf2 = &impl{}
    CallIntf[Intf](intf2)

实例化汇编(包含对runtime.assertI2I()的调用):

    LEAQ    type."".impl(SB), AX
    CALL    runtime.newobject(SB)
    MOVQ    AX, ""..autotmp_13+24(SP)
    LEAQ    go.itab.*"".impl,"".Intf2(SB), BX
    LEAQ    type."".Intf(SB), AX
    CALL    runtime.convI2I(SB)
    MOVQ    AX, BX
    MOVQ    ""..autotmp_13+24(SP), CX
    LEAQ    ""..dict.CallIntf["".Intf](SB), AX
    CALL    "".CallIntf[go.shape.interface { Do() }_0](SB)

生成的单态函数 asm(包含对 runtime.assertI2I() 的调用):

    TEXT    "".CallIntf[go.shape.interface { Do() }_0](SB), DUPOK|ABIInternal, $32-24
    MOVQ    CX, "".intf+56(SP)
    LEAQ    type."".Intf(SB), AX
    CALL    runtime.assertI2I(SB)
    MOVQ    24(AX), CX
    MOVQ    "".intf+56(SP), AX
    CALL    CX
    MOVQ    24(SP), BP
    ADDQ    $32, SP
    RET

手动单形asm(不调用runtime.assertI2I()):

    TEXT    "".CallIntf_mono(SB), ABIInternal, $16-16
    MOVQ    AX, "".intf+24(FP)
    MOVQ    BX, "".intf+32(FP)
    MOVQ    24(AX), CX
    MOVQ    BX, AX
    CALL    CX
    MOVQ    8(SP), BP
    ADDQ    $16, SP
    RET

问题:为什么生成的单态函数使用runtime.assertI2I(),而手动单态函数没有?在什么情况下调用者会使用需要转换的类型?

【问题讨论】:

  • 不太清楚你的意思。如果将 nil 作为参数传递,则 CallIntf_mono() 和 CallIntf[T Intf]() 都会发生恐慌。不过,Monomorph 会抛出更粗糙的 SIGSEGV。
  • 是的,但我不明白它与问题有何关系。 CallIntf_mono() 和 CallIntf[T Intf]() 都出现恐慌,因此额外调用 runtime.assertI2I() 在这个意义上没有显着差异。

标签: go generics assembly


【解决方案1】:

我无法在其他地方得到确认,所以我只想在这里发表我的直觉。

您看到的可能是由于当前基于 GC Stenciling 的 Go 泛型实现。 GC 代表垃圾收集器。

执行摘要是:

[...],为了实现的简单性(和性能),我们没有针对所有可能的类型参数的通用函数/方法的单一编译。相反,我们在具有相同 gcshape 的类型参数集之间共享通用函数/方法的实例化。

gcshape 是一组对垃圾收集器“看起来相同”的类型。更正式地说,具有相同基础类型或指针的类型。

在 ASM 中看到的标识符 go.shape.interface { Do() }_0Intf 接口的 gcshape,用作类型参数约束,其中 go.shape 是内置包,interface { Do() }Intf 的底层类型。

根据我的理解,以上暗示泛型函数在实例化时并不是真正的单态化。所以伪寄存器SB 最终具有接口的gcshape,而不是接口本身。为此,使用runtime.assertI2I(SB)保证两个接口兼容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 2012-08-28
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    • 2018-01-11
    相关资源
    最近更新 更多