我正在阅读this article并认为一切都非常清楚,直到我偶然发现:
同样,大多数真实的Scheme系统使用稍微不同的实现;例如,如果
GET_PAIR
减去x
的低位,而不是将它们屏蔽掉,优化器通常能够将该减法与我们引用的结构成员的偏移量相加,使修改后的指针快速用作未修改的指针。
如何实现这种减法以及优化器如何像未经修改的指针一样快速地修改指针?
答案 0 :(得分:4)
本文介绍的技巧是将类型信息编码为8字节对齐指针的未使用的三个最低位。使用此信息查找类型后,
#define PAIR_P(x) (((int) (x) & 7) == 2)
必须在将指针再次用作地址之前清除这些附加位。
#define GET_PAIR(x) ((struct pair *) ((int) (x) & ~7))
注意,此时我们已经知道了类型,因此我们知道三个最低有效位的值。它们将始终为0b010
(十进制2)。因此,作者建议不要写((int) (x) & ~7)
而是写((int) (x) - 2)
。这个想法是,如果你编写这样的代码,
if (PAIR_P(x))
{
SCM * thing = GET_PAIR(x)->cdr;
/* Use the thing… */
}
因为我们正在访问cdr
所指向的struct pair
内的x
成员(在清除低位之后),编译器将生成代码以适当地调整指针。这样的事情。
SCM * thing = (SCM *) ((char *)((int) (x) - 2)) + offsetof(struct pair, cdr));
由于整数加法和减法的相关性,我们可以省略一级括号并得到(不显示不产生机器代码的外部指针强制转换)
(int) (x) - 2 + offsetof(struct pair, cdr)
其中,2
和offsetof(struct pair, cdr)
都是编译时常量,可以折叠成一个常量。如果我们要求car
成员(其偏移量为0),这个技巧将无济于事,但每隔一段时间的帮助也不会太糟糕。
现代优化工具可能能够在我们刚刚测试(x & 7) == 2
,x & ~7
相当于x - 2
之后自行弄明白,因此可能不需要这个技巧这些天来了。不过,您希望在依赖它之前测量它。