Next: , Up: Type Information


22.1 GTY(())的内部

有时候C代码不足以完全描述类型结构体,这时可以使用GTY选项和额外标记来提供额外的信息。一些选项接受一个参数,其可以为字符串或者类型名。如果一个选项不需要参数,则可以完全省略参数,或者提供一个空字符串作为参数。例如,GTY ((skip))GTY ((skip("")))是等价的。

当参数为字符串时,通常为一个C代码片段。有四种特定换码符可以在字符串中使用,用来指定被标记的数据结构体:

%h
当前结构体。
%1
直接包含当前结构体的结构体。
%0
包含当前结构体的最外层结构体。
%a
[i1][i2]...形式的部分表达式,用来索引当前被标记的数组项。

例如,假设有一个结构体

     struct A {
       ...
     };
     struct B {
       struct A foo[12];
     };

并且bstruct B类型的变量。当标记‘b.foo[11]’时,%h将扩展为‘b.foo[11]’,%0%1都会扩展为‘b’,%a会扩展为‘[11]’。

由于原始的C中,相邻的字符串会被连接;这对于复杂的表达式是有帮助的。

     GTY ((chain_next ("TREE_CODE (&%h.generic) == INTEGER_TYPE"
                       " ? TYPE_NEXT_VARIANT (&%h.generic)"
                       " : TREE_CHAIN (&%h.generic)")))

可用的选项:

length ("expression")
有两个地方需要显示的告诉类型机构一个数组的长度。第一种情况是当一个结构体结束于一个可变长度数组,像这样:
          struct rtvec_def GTY(()) {
            int num_elem;         /* number of elements */
            rtx GTY ((length ("%h.num_elem"))) elem[1];
          };

在这种情况下,length 选项用来覆盖指定数组的长度(通常本应该为 1)。选项的参数是C代码片断用来计算长度。

第二种情况是当一个结构体或者全局变量包含一个指向数组的指针,像这样:

          tree *
            GTY ((length ("%h.regno_pointer_align_length"))) regno_decl;

在这种情况下,regno_decl已经通过类似下面的方式被分配:

            x->regno_decl =
              ggc_alloc (x->regno_pointer_align_length * sizeof (tree));

并且 length 提供了指定域的长度。

lenght 的第二种用法还包括在全局变量上,像这样:

     
       static GTY((length ("reg_base_value_size")))
         rtx *reg_base_value;


skip
如果skip应用在一个域上,则类型机构将会忽略该域。这有些危险; 唯一安全的使用方式是在一个联合体中,当一个域确实不会被使用到的时候。


desc ("expression")
tag ("constant")
default
类型机构需要知道union的哪一个域是当前活跃的。这是通过赋给每个域一个常数tag值,并且使用desc指定一个判别器来完成的。由desc给出的表达式的值用来与每个tag值比较,每个 tag值应该不同。如果没有tag匹配,则会使用标记为default的域。

desc选项中,“当前结构体”是指要进行判别的联合体,可以使用%1来指定。tag选项没有换码符可用,因为其为常数。

例如,

          struct tree_binding GTY(())
          {
            struct tree_common common;
            union tree_binding_u {
              tree GTY ((tag ("0"))) scope;
              struct cp_binding_level * GTY ((tag ("1"))) level;
            } GTY ((desc ("BINDING_HAS_LEVEL_P ((tree)&%0)"))) xscope;
            tree value;
          };

在这个例子中,当BINDING_HAS_LEVEL_P应用到struct tree_binding*时,其值会被假设为0或者1。如果是1,类型机制则会认为域level存在,如果是0,则会认为域scope存在。


param_is (type)
use_param
有时候,定义某种数据结构作为通用指针(也就是PTR),并且与特定类型一起使用是比较方便的。param_is指定了所指向的真正类型,use_param说明了该类型应该放在通用数据结构的哪个地方。

例如,为了让htab_t指向trees,则应该像这样来写htab_t的定义:

          typedef struct GTY(()) {
            ...
            void ** GTY ((use_param, ...)) entries;
            ...
          } htab_t;

然后按这种方式声明变量:

            static htab_t GTY ((param_is (union tree_node))) ict;


paramn_is (type)
use_paramn
use_param9. 在更复杂的情况下,数据结构可能需要工作在多个不同类型之上,而且这些类型也不必都是指针。对于这样的,可以使用param1_isparam9_is来指定由use_param1use_param9标识的实际类型域。


use_params
当结构体包含另一个参数化的结构体时,不需要做特别的处理,内部结构体会继承外部的参数。当结构体包含指向一个参数化的结构体的指针时,类型机构不会自动检测到(是应该可以的,只是还没有实现),所以需要告诉类型机构所指向的结构体将使用外部结构体的相同参数。这可以通过使用usr_params选项来标识指针。


deletable
deletable应用到全局变量上时,表示当垃圾收集运行时,不需要标记由该变量指向的任何对象,可以只是将其设为NULL。这可以用来维护一个可以重用的空闲结构体列表。


if_marked ("expression")
假设你想要一些类别的对象是唯一的,并且为此你将它们放在了哈希表中。如果垃圾搜集标记了哈希表,这些对象将永远不会被释放掉,即使最后一个引用也不存在。对此GCC有特定的处理方式:如果你使用if_marked选项在一个全局哈希表上,GCC将会对每个哈希表项调用该选项参数命名的函数。如果函数返回非0,哈希表项将按照通常的方式被标记,如果返回0,则哈希表项将会被删除。

函数ggc_marded_p可以用来判断一个元素是否已经被标记。实际上,通常的情况是使用if_marked ("ggc_marked_p")


mark_hook ("hook-routine-name")
如果用在结构体或者联合体类型上,给出的(双引号之间的)hook-routine-name则为一个函数名,其在垃圾搜集器刚刚标记数据为可达(reachable)时会被调用。该函数不应该改变数据,或者调用任何ggc函数。它的唯一参数是一个指向刚刚被标记的结构体或联合体的指针。


maybe_undef
当应用到一个域时,maybe_undef表示可以允许该域所指向的结构体没有被定义,只要该域总是为NULL。这可以用来避免要求后端去定义一些可选的结构体。该选项对语言前端不起作用。


nested_ptr (type, "to expression", "from expression")
类型设备期望所有指针都指向一个对象的起始处。有时候出于抽象目的,使用指向对象内部的指针是比较方便的。只要能够对原始对象和指针进行相互转换,这样的指针还是可以使用的。type是原始对象的类型,to expression返回给定原始对象的指针,from expression返回给定指针的原始对象。指针可以使用%h转换符得到。


chain_next ("expression")
chain_prev ("expression")
chain_circular ("expression")
让类型设备知道对象是否经常被链接在长的链表中是有帮助作用的。这可以让其使用遍历链表的方式来替代递归调用,从而使得生成的代码使用很少的栈空间。chain_next是链表中的下一项,chain_prev是前一项。对于单 向链表,只使用chain_next;对于双向链表,两者都使用。设备要求对一个项求chain_prev,然后 chain_next,可以得到原始的项。


reorder ("function name")
一些数据结构依赖于相应的指针顺序。如果预编译头文件设备需要改变顺序,其将会调用由reorder选项指定的函数。函数必须接收4个参数,‘void *, void *, gt_pointer_operator, void *’。第一个参数是指 向更新对象的结构体的指针,或者对象本身,如果没有包含的结构体。第二个参数为一个cookie,目前被忽略。第三个参数是一个函数,给定指针,将会更新该指针为正确的新值。第四个参数是一个cookie,且必须传给第二个参数。

PCH无法处理依赖于指针绝对值得数据结构。reorder函数代价很高。在可能的情况下,最好依赖于数据的属性,像ID号或者字符串的哈希值。


special ("name")
special选项用来标记类型必须由特定情况的机制来处理。参数是特定情况的名字。详细信息参见gengtype.c。应避免添加新的特定情况,除非没有别的办法。