首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >cmm调用格式(整数-gmp示例)

cmm调用格式(整数-gmp示例)
EN

Stack Overflow用户
提问于 2013-04-17 18:34:03
回答 2查看 1.1K关注 0票数 63

我一直在查看整型gmp源代码,以了解如何以cmm的方式实现GHC Primops页面上所记录的外国原始业务。我知道使用llvm攻击或fvia-C/gcc来实现它们的技术--这是我理解interger-gmp库使用的第三种方法的一种学习经验。

因此,我在MSFT页面(pdf连结)上查阅了CMM教程,浏览了GHC CMM页面,仍然有一些未回答的问题(很难在不深入CMM的情况下保留所有这些概念,这就是我现在正在做的)。有一个来自整型bmp cmm文件的代码片段

代码语言:javascript
复制
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报头中所定义的

代码语言:javascript
复制
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行是开始,以防你有1/0混淆)。更具体而言:

  • 为什么ALLOC_PRIM_N (字节,乐趣,arg)的函数调用格式是这样的?
  • 为什么P会被这样操纵?

据我理解,这个函数(通过查看Prim.hs中的函数签名)接受一个int,并返回一个(int,字节数组)(分别存储在代码中的sp中)。

对于任何想知道在if block中内联调用的人来说,这是si函数的cmm实现。我的猜测是,如果您通过ccall调用对象文件中定义的函数,它就不能内联(这是有意义的,因为它是对象代码,而不是中间代码- LLVM方法似乎更适合通过LLVM IR进行内联)。因此,优化是定义要内联的函数的cmm表示。如果这个猜测是错误的,请纠正我。

我们将非常感谢对第5-9行的解释。关于整数-gmp文件中定义的其他宏,我有更多的问题,但在一篇文章中可能要求太多了。如果您可以使用Haskell wiki页面或博客来回答这个问题(您可以将链接作为答案发布),这将是非常感谢的(如果您这样做了,我也会感谢一个整数-gmp的cmm宏(如GMP_TAKE2_RET1)的逐步遍历)。

EN

回答 2

Stack Overflow用户

发布于 2014-09-22 14:54:39

这些行在Haskell堆上分配了一个新的ByteArray#,因此要了解它们,首先需要了解一下GHC的堆是如何管理的。

  • 每个功能(=执行Haskell代码的OS线程)都有自己的专用托儿所,这是堆中的一个区域,它将像这样进行正常的、小的分配。对象被顺序地分配到这个区域,从低地址到高地址,直到功能尝试做出超过苗圃中剩余空间的分配,这会触发垃圾收集器。
  • 所有堆对象都与字大小的倍数对齐,即32位系统上的4个字节和64位系统上的8个字节。
  • Cmm级寄存器Hp指向托儿所中分配的最后一个单词(开头)。HpLim指向可以在托儿所中分配的最后一个单词。(HpLim也可以由另一个线程设置为0,以停止GC的世界,或者发送异步异常。)
  • https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Storage/HeapObjects提供了有关各个堆对象布局的信息。特别要注意的是,每个堆对象都以一个信息指针开头,该指针(除其他外)标识它是哪种堆对象。

Haskell类型的ByteArray#是用堆对象类型ARR_WORDS实现的。ARR_WORDS对象只是由(一个信息指针后面跟着)一个大小(字节)和任意数据(有效载荷)组成。GC不解释有效负载,因此它不能存储指向Haskell堆对象的指针,但可以存储任何其他内容。SIZEOF_StgArrWords是所有ARR_WORDS堆对象共有的头的大小,在这种情况下,有效负载只是一个单词,所以SIZEOF_StgArrWords + WDS(1)是我们需要分配的空间的数量。

ALLOC_PRIM_N (SIZEOF_StgArrWords + WDS(1),integer_cmm_int2Integerzh,val)扩展为

代码语言:javascript
复制
Hp = Hp + (SIZEOF_StgArrWords + WDS(1));
if (Hp > HpLim) {
    HpAlloc = SIZEOF_StgArrWords + WDS(1);
    goto stg_gc_prim_n(integer_cmm_int2Integerzh, val);
}

第一行增加惠普分配的数量。第二行检查堆溢出。第三行记录我们试图分配的金额,这样GC就可以撤销它。第四行调用GC。

第四行是最有趣的。参数告诉GC如何在垃圾回收完成后重新启动线程:它应该使用参数val重新调用integer_cmm_int2Integerzh。_n( stg_gc_prim_n中的"_n“和ALLOC_PRIM_N中的"_N”)意味着val是一个非指针参数(在本例中是Int#)。如果val是指向Haskell堆对象的指针,则GC需要知道它是活动的(因此它不会被收集),并使用对象的新地址重新调用我们的函数。在这种情况下,我们将使用_p变体。还有一些变体,如用于多指针参数的_pp,用于Double#参数的_d等。

在第5行之后,我们成功地分配了一个SIZEOF_StgArrWords + WDS(1)字节块,记住,Hp指向它的最后一个单词。所以,p= Hp - SIZEOF_StgArrWords将p设置为这个块的开头。第8行填充p的info指针,将新创建的堆对象标识为ARR_WORDS。CCCS是当前的成本中心堆栈,仅用于分析。启用分析时,每个堆对象都包含一个额外的字段,该字段基本上标识谁负责其分配。在非分析构建中,没有CCCS,SET_HDR只设置信息指针。最后,第9行填充ByteArray#的size字段。函数的其余部分填充有效负载并返回符号值和ByteArray#对象指针。

因此,这最终更多地是关于GHC堆而不是Cmm语言,但我希望它能有所帮助。

票数 5
EN

Stack Overflow用户

发布于 2014-09-24 06:32:12

所需知识

为了实现运算和逻辑运算,计算机在其CPU (Central )中采用了数字电路,称为ALU (算术逻辑单元)。ALU从输入寄存器加载数据。处理器寄存器是在位于CPU芯片E 220的SRAM(静态随机存取存储器)中实现的L1 cache (数据请求在3 CPU时钟时钟内的数据请求)中的内存存储。一个处理器通常包含几种寄存器的,通常根据它们能容纳的位数来区分。

数字是用表示的离散位,可以保持有限的数值。数字通常遵循原语类型,由编程语言(在哈斯克尔)公开:

代码语言:javascript
复制
 8 bit numbers = 256 unique representable values
16 bit numbers = 65 536 unique representable values
32 bit numbers = 4 294 967 296 unique representable values
64 bit numbers = 18 446 744 073 709 551 616 unique representable values

针对这些类型的固定精度算法已经在硬件中实现了.字大小是指计算机的CPU一次可处理的位数。对于x86体系结构,这是32位和x64,这是64位。

IEEE754定义了{16,32,64,128}位数字的浮点数标准,例如32位点数(有4 294 967 296个唯一值),至少可以保持近似值-3.402823e38到3.402823e38,其精度至少<代码>E 2547<代码>E 256浮点数>E 157位数。

此外

缩写GMP指的是GNU多精度算法库,并添加了对软件模拟的任意精度算法的支持。Glasgow Haskell编译器 Integer实现使用此方法。

GMP的目标是在所有操作数的大小上比任何其他大型库都更快。这样做的一些重要因素是:

  • 使用完整的单词作为基本算术类型。
  • 对于不同的操作数,使用不同的算法;对于非常大的数字,速度较快的算法通常对较小的数字使用较慢的算法。
  • 高度优化的汇编语言代码最重要的内环,专门为不同的处理器。

回答

对于某些人来说,Haskell可能有点难以理解语法,下面是javascript版本

代码语言:javascript
复制
var integer_cmm_int2Integerzh = function(word) {
  return WORDSIZE == 32 
    ? goog.math.Integer.fromInt(word))
    : goog.math.Integer.fromBits([word.getLowBits(), word.getHighBits()]);
};

goog是使用的Google闭包库类位于Math.Integer中。称为职能:

代码语言:javascript
复制
goog.math.Integer.fromInt = function(value) {
  if (-128 <= value && value < 128) {
    var cachedObj = goog.math.Integer.IntCache_[value];
    if (cachedObj) {
      return cachedObj;
    }
  }

  var obj = new goog.math.Integer([value | 0], value < 0 ? -1 : 0);
  if (-128 <= value && value < 128) {
    goog.math.Integer.IntCache_[value] = obj;
  }
  return obj;
};
goog.math.Integer.fromBits = function(bits) {
  var high = bits[bits.length - 1];
  return new goog.math.Integer(bits, high & (1 << 31) ? -1 : 0);
};

这并不完全正确,因为返回类型应该是return (s,p);

  • S是值
  • P是符号

为了修复这个问题,应该创建GMP包装器。这是在Haskell到JavaScript编译器项目(源链接)中完成的。

第5-9行

代码语言:javascript
复制
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;

如下所示

  • 将空间分配为新单词
  • 创建指向它的指针
  • 设置指针值
  • 设置指针类型大小
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16067523

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档