为什么类型参数中的基本接口导致产生的单态函数使用Runtime.asserti2i()?

发布于 2025-01-23 03:08:28 字数 1719 浏览 0 评论 0原文

多态函数(完整代码请参见在此处):

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)

实例: Code> 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 Time.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.asserti.asserti.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(),而手动单态函数之一则没有?在这种情况下,呼叫者将使用需要断言的类型?

Polymorphic function (full code see here):

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

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

Manual monomorph:

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

Instantiation:

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

Instantiation asm (contains a call to 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)

Generated monomorphic function asm (contains a call to 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

Manual monomorph asm (does not call 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

Question: Why does the assembly of the generated monomorphic function use runtime.assertI2I(), while the one of the manual monomorphic function does not? In which case caller would use a type which needs to be asserted?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嘿哥们儿 2025-01-30 03:08:28

我无法在其他地方得到确认,所以我只是要在这里发布直觉。

您可能看到的可能是由于当前的GO Generics实施,该实施基于 gc固定。 GC代表垃圾收集器。

执行摘要是:

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

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

标识符go.shape.interface {do()} _0在您的ASM中看到的是intf接口的gcshape,用作类型参数约束,其中> GO.Shape是一个内置软件包,接口{do()}intf的基础类型。

基于我的理解,以上意味着通用函数并不是实例化后真正单体化。因此,pseudo-register sb最终具有接口的GCSHAPE,而不是接口本身。因此,runtime.asserti2i(sb)用于确保两个接口兼容。

无论如何,这都是所有实施详细信息。 ASM可能会随着未来的GO发布,甚至可以发行。在撰写本文时,该断言仍然存在于GO 1.18.3编译的代码中。

I couldn't get confirmation elsewhere, so I'm just going to post my intuition here.

What you see is likely due to the current implementation of Go generics, which is based on GC Stenciling. GC stands for Garbage Collector.

The executive summary is:

[...], for simplicity (and performance) of implementation, we do not have a single compilation of a generic function/method for all possible type arguments. Instead, we share an instantiation of a generic function/method among sets of type arguments that have the same gcshape.

A gcshape is a group of types that "look the same" to the garbage collector. More formally, types that have the same underlying type, or pointers.

The identifier go.shape.interface { Do() }_0 seen in your ASM is the gcshape of the Intf interface, used as a type parameter constraint, where go.shape is a builtin package and interface { Do() } is the underlying type of Intf.

Based on my understanding, the above implies that generic functions aren't truly monomorphized upon instantiation. So the pseudo-register SB ends up having the gcshape of the interface, not the interface itself. For this reason, runtime.assertI2I(SB) is used to ensure that the two interfaces are compatible.

Anyway this is all implementation details. The ASM may change with future Go releases, and even point releases. At the time of writing, the assertion is still present in Go 1.18.3 compiled code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文