Next: GGC Roots, Up: Type Information
GTY(())
的内部有时候C代码不足以完全描述类型结构体,这时可以使用GTY
选项和额外标记来提供额外的信息。一些选项接受一个参数,其可以为字符串或者类型名。如果一个选项不需要参数,则可以完全省略参数,或者提供一个空字符串作为参数。例如,GTY ((skip))
和GTY ((skip("")))
是等价的。
当参数为字符串时,通常为一个C代码片段。有四种特定换码符可以在字符串中使用,用来指定被标记的数据结构体:
%h
%1
%0
%a
[i1][i2]...
形式的部分表达式,用来索引当前被标记的数组项。
例如,假设有一个结构体
struct A { ... }; struct B { struct A foo[12]; };
并且b
是struct 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;
param
n_is (
type)
use_param
nuse_param9
.
在更复杂的情况下,数据结构可能需要工作在多个不同类型之上,而且这些类型也不必都是指针。对于这样的,可以使用param1_is
到param9_is
来指定由use_param1
到use_param9
标识的实际类型域。
use_params
usr_params
选项来标识指针。
deletable
deletable
应用到全局变量上时,表示当垃圾收集运行时,不需要标记由该变量指向的任何对象,可以只是将其设为NULL
。这可以用来维护一个可以重用的空闲结构体列表。
if_marked ("
expression")
if_marked
选项在一个全局哈希表上,GCC将会对每个哈希表项调用该选项参数命名的函数。如果函数返回非0,哈希表项将按照通常的方式被标记,如果返回0,则哈希表项将会被删除。
函数ggc_marded_p
可以用来判断一个元素是否已经被标记。实际上,通常的情况是使用if_marked ("ggc_marked_p")
。
mark_hook ("
hook-routine-name")
maybe_undef
maybe_undef
表示可以允许该域所指向的结构体没有被定义,只要该域总是为NULL
。这可以用来避免要求后端去定义一些可选的结构体。该选项对语言前端不起作用。
nested_ptr (
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。应避免添加新的特定情况,除非没有别的办法。