为啥没有“外国进口 prim 不安全”?

Posted

技术标签:

【中文标题】为啥没有“外国进口 prim 不安全”?【英文标题】:Why is there no 'foreign import prim unsafe'?为什么没有“外国进口 prim 不安全”? 【发布时间】:2017-05-22 13:04:29 【问题描述】:

这是我之前的问题here 的后续。我已经能够根据Reid Barton's answer 得到一些工作,但我注意到在核心中我看到了__pkg_ccall_GC

              case __pkg_ccall_GC hashabler-2.0.0 sipRound_s_x2 Word#
                                          -> Word#
                                          -> Word#
                                          -> Word#
                                          -> (# Word#, Word#, Word#, Word# #)
                     ww1 ww2 ww3 (xor# ww4 b1)

我认为您对“安全” ffi 通话的期望是什么。然而,不允许在外部导入字符串中添加“不安全”(尽管错误消息没有说明原因):

src/Data/Hashabler/SipHash.hs:60:1: error:
    • The safe/unsafe annotation should not be used with `foreign import prim'.
    • When checking declaration:
        foreign import prim unsafe "static sipRound_s_x4" sipRound_s_x4#
          :: Word#
             -> Word# -> Word# -> Word# -> (# Word#, Word#, Word#, Word# #)

我的外国程序只是一点点但有点混乱,所以我不认为我想要_GC 给我的任何东西。我看过的一些相关的 GHC 源代码、FWIW 和背景:

compiler/prelude/ForeignCall.hs:只有“Risky”省略了“_GC”

data Safety
  = PlaySafe            -- Might invoke Haskell GC, or do a call back, or
                        -- switch threads, etc.  So make sure things are
                        -- tidy before the call. Additionally, in the threaded
                        -- RTS we arrange for the external call to be executed
                        -- by a separate OS thread, i.e., _concurrently_ to the
                        -- execution of other Haskell threads.

  | PlayInterruptible   -- Like PlaySafe, but additionally
                        -- the worker thread running this foreign call may
                        -- be unceremoniously killed, so it must be scheduled
                        -- on an unbound thread.

  | PlayRisky           -- None of the above can happen; the call will return
                        -- without interacting with the runtime system at all
  deriving ( Eq, Show, Data )
        -- Show used just for Show Lex.Token, I think

我还在 GHC 树中看到了一些 foreign import prim unsafe... safe,尽管我认为这是死代码。例如testsuite/tests/printer/Ppr046.hs.

所以我的问题是:

    在这种情况下,__pkg_ccall_GC__pkg_ccall 生成的代码之间有什么区别(我正在做 foreign import prim 而不是 ccall)?和here描述的一样吗? 为什么似乎不支持foreign import prim unsafe? 假设我理解 (1):有没有办法解决这个问题,既能有效地返回多个值,又能避免 (1) 中发生的任何簿记?

编辑:查看-ddump-asm 的程序集清楚地表明没有发生任何事情(不应该害怕查看程序集),支持 Reid Barton 在下面的评论:

movq %rdi,%rax
movq %r8,%rdi 
xorq %r9,%rdi
movq %rsi,%rcx
movq %rax,%rsi
movq %r14,%rax
movq %rcx,%r14
movq %rbx,%rcx
movq %rax,%rbx
movq %r9,-8(%rbp)
movq %rcx,(%rbp)
addq $-16,%rbp
jmp sipRound_s_x2

顶部的xorq 对应于haskell xor。不过,所有那些movq 看起来确实是个无赖……

【问题讨论】:

如果您查看生成的 Cmm,则在安全呼叫周围没有任何 suspendThread/resumeThread 内容。我不知道为什么它在Core中显示__pkg_ccall_GC,可能只是一个显示错误。 不是那些movq 在尾调用之前将参数放入正确的寄存器吗?在我看来,它们并不多余。 可能值得提交一个错误是的——看起来prim 导入被解析器标记为PlaySafe,这可能大部分被忽略了,并且在代码生成期间肯定会被忽略。但只要不被忽视,就可能存在错误。 您必须进行全局寄存器分配以避免参数改组。我不认为 ghc 这样做。你试过 llvm 代码生成器吗? 是的,不过我很难比较它们 【参考方案1】:

正如 Reid Barton 指出的那样,__pkg_ccall_GC 没有任何意义。生成的代码不会像您在 safe FFI 调用中看到的那样进行记账。

【讨论】:

以上是关于为啥没有“外国进口 prim 不安全”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥哈罗小程序明明发出了信息为啥过后没有了

为啥我没有 NotSerializableException?

如果函数没有明确使用'ret',为啥没有返回值

为啥 Firestore 有时不返回任何内容,没有错误,啥也没有?

为啥没有返回语句时没有编译器错误?

为啥没有调用 didDeselectRowAtIndexPath