GHC 中如何实现整数比较?

Alb*_*zia 14 haskell ghc

起初,我想研究一下如何Integer从类中派生出来Ord

\n

我在 GHC.Classes 中得到了这个定义

\n
instance Ord Integer where\n    (<=) = leInteger\n    (>)  = gtInteger\n    (<)  = ltInteger\n    (>=) = geInteger\n    compare = compareInteger\n
Run Code Online (Sandbox Code Playgroud)\n

compareInteger指向另一个源文件,GHC.Integer.Type. 它的定义如下\xe2\x80\xaf:

\n
compareInteger :: Integer -> Integer -> Ordering\ncompareInteger (Jn# x)  (Jn# y) = compareBigNat y x\ncompareInteger (S#  x)  (S#  y) = compareInt#   x y\ncompareInteger (Jp# x)  (Jp# y) = compareBigNat x y\ncompareInteger (Jn# _)  _       = LT\ncompareInteger (S#  _)  (Jp# _) = LT\ncompareInteger (S#  _)  (Jn# _) = GT\ncompareInteger (Jp# _)  _       = GT\n
Run Code Online (Sandbox Code Playgroud)\n

S#适用于固定大小的整数Jn#Jp#任意大小的整数。

\n

在 GHC.Classes (来自 ghc-prim 包)中,我能够找到compareInt#. 像这样不寻常类型的出现Int#标志着我离成功越来越近了。

\n
compareInt# :: Int# -> Int# -> Ordering\ncompareInt# x# y#\n    | isTrue# (x# <#  y#) = LT\n    | isTrue# (x# ==# y#) = EQ\n    | True                = GT\n
Run Code Online (Sandbox Code Playgroud)\n

继续深入,我得到了运算符的定义(GHC.Prim 模块)

\n
infix 4 <#\n(<#) :: Int# -> Int# -> Int#\n(<#) = (<#)\n
Run Code Online (Sandbox Code Playgroud)\n

但这是我能够达到的深度。<#指的是它自己。我们不知道 xe2x80x99 正在做什么。

\n

isTrue#只是一个函数,True如果其参数为\xe2\x80\x9c,1#则返回;如果0#\xe2\x80\x9d,则返回 False)

\n

我在哪里可以找到源代码,完成工作的实际位置\xe2\x80\xaf?\n最底部是否有一些程序集\xe2\x80\xaf?我在哪里可以找到这个神圣的地方\xe2\x80\xaf?

\n

Nou*_*are 14

首先,从技术上讲,当你进入模块时,GHC.Integer.Type你就离开了 Haskell 的领域,进入了 GHC 使用的当前实现的领域,所以这个问题是专门关于 GHC Haskell 的。

所有基本操作都作为(<#)您在模块中找到的递归循环来实现GHC.Prim。从那里,文档告诉我们下一个要查看的位置是primops.txt.pp名称下列出的文件IntLtOp

然后前面提到的文档说有两组 primops:内联和外联。内联 primop 在从 STG 到 Cmm(这是 GHC 使用的两种内部表示)的转换过程中被解析,并且可以在模块中找到GHC.StgToCmm.Prim。实际上,案例IntLtOp已在那里列出mo_wordSLt,并且主要使用取决于平台的功能进行内联转换。

mo_wordSLt函数在模块中定义,GHC.Cmm.MachOp其中包含引用:

机器级primops;我们可以合理地委托给本机代码生成器来处理。

mo_wordSLt函数生成数据类型MO_S_Lt的构造函数MachOp。因此,我们可以进一步研究本机代码生成器,看看如何将其转换为低级指令。平台有相当多的选择:SPARCAArch64LLVMCPPCX86(我通过 GitLab 上的搜索功能找到了所有这些)。

X86 是最流行的平台,所以我会继续那里。该实现使用condIntReg辅助函数,其定义如下:

condIntReg :: Cond -> CmmExpr -> CmmExpr -> NatM Register

condIntReg cond x y = do
  CondCode _ cond cond_code <- condIntCode cond x y
  tmp <- getNewRegNat II8
  let
        code dst = cond_code `appOL` toOL [
                    SETCC cond (OpReg tmp),
                    MOVZxL II8 (OpReg tmp) (OpReg dst)
                  ]
  return (Any II32 code)
Run Code Online (Sandbox Code Playgroud)

同样有趣的是 的定义condIntCode,它取决于条件的操作数。这是一个很大的函数,所以我不会在这里重现完整的代码,但一般情况会产生一条CMP指令:

-- anything vs anything
condIntCode' _ cond x y = do
  platform <- getPlatform
  (y_reg, y_code) <- getNonClobberedReg y
  (x_op, x_code) <- getRegOrMem x
  let
        code = y_code `appOL`
               x_code `snocOL`
                  CMP (cmmTypeFormat (cmmExprType platform x)) (OpReg y_reg) x_op
  return (CondCode False cond code)
Run Code Online (Sandbox Code Playgroud)