我一直在检查integer-gmp源代码,以了解如何根据GHC Primops page中记录的 cmm 实现外国 primops 。我知道使用llvm hack或 fvia-C/gcc 来实现它们的技术——这对我来说更像是一种学习经验,可以理解 interger-gmp 库使用的第三种方法。
因此,我在 MSFT 页面(pdf 链接)上查找了 CMM 教程,浏览了GHC CMM 页面,但仍然有一些未解决的问题(如果不深入研究 CMM,很难将所有这些概念牢记在心,这就是我现在正在做的事情)。integer-bmp cmm 文件中有以下代码片段:
integer_cmm_int2Integerzh (W_ val)
{
W_ s, p; /* to avoid aliasing */
ALLOC_PRIM_N (SIZEOF_StgArrWords + WDS(1), integer_cmm_int2Integerzh, val);
p = Hp - SIZEOF_StgArrWords;
SET_HDR(p, stg_ARR_WORDS_info, CCCS);
StgArrWords_bytes(p) = SIZEOF_W;
/* mpz_set_si is inlined here, makes things simpler */
if (%lt(val,0)) {
s = -1;
Hp(0) = -val;
} else {
if (%gt(val,0)) {
s = 1;
Hp(0) = val;
} else {
s = 0;
}
}
/* returns (# size :: Int#,
data :: ByteArray#
#)
*/
return (s,p);
}
如ghc cmm 标头中所定义:
W_ is alias for word.
ALLOC_PRIM_N is a function for allocating memory on the heap for primitive object.
Sp(n) and Hp(n) are defined as below (comments are mine):
#define WDS(n) ((n)*SIZEOF_W) //WDS(n) calculates n*sizeof(Word)
#define Sp(n) W_[Sp + WDS(n)]//Sp(n) points to Stackpointer + n word offset?
#define Hp(n) W_[Hp + WDS(n)]//Hp(n) points to Heap pointer + n word offset?
我不明白第 5-9 行(如果您有 1/0 的困惑,第 1 行是开始)。进一步来说:
- 为什么ALLOC_PRIM_N(bytes,fun,arg)的函数调用格式是那样的?
- 为什么 p 被这样操纵?
我理解的函数(通过查看Prim.hs中的函数签名)接受一个 int,并返回一个 (int, byte array) (分别存储在s
代码p
中)。
对于任何想知道内联调用的人if block
,它是 gmp mpz_init_si 函数的 cmm 实现。我的猜测是,如果您通过 ccall 调用在目标文件中定义的函数,则它不能被内联(这是有道理的,因为它是目标代码,而不是中间代码 - LLVM 方法似乎更适合通过 LLVM IR 内联)。因此,优化是定义要内联的函数的 cmm 表示。如果这个猜测是错误的,请纠正我。
非常感谢对第 5-9 行的解释。我对 integer-gmp 文件中定义的其他宏有更多疑问,但在一篇文章中可能会问得太多。如果您可以通过 Haskell wiki 页面或博客回答问题(您可以发布链接作为答案),那将不胜感激(如果您这样做了,我也将不胜感激整数的逐步演练-gmp cmm 宏,例如GMP_TAKE2_RET1
)。