getelementptr具有-1作为第一个索引操作数

时间:2016-04-06 06:05:06

标签: compilation llvm llvm-ir

我正在阅读Clang生成的nginx的IR。在函数ngx_event_expire_timers中,有一些getelementptr指令以i64 -1作为第一个索引操作数。例如,

%handler = getelementptr inbounds %struct.ngx_rbtree_node_s, %struct.ngx_rbtree_node_s* %node.addr.0.i, i64 -1, i32 2

我知道第一个索引操作数将用作第一个操作数的偏移量。但负偏移意味着什么?

2 个答案:

答案 0 :(得分:1)

GEP指令完全没有负指数。 在这种情况下,你有类似的东西:

node arr[100]; 
node* ptr = arr[50]; 
if ( (ptr-1)->value == ptr->value) 
  // then ...

具有负指数的GEP只计算基指针到另一个方向的偏移量。没有什么问题。

答案 1 :(得分:0)

考虑到在nginx源代码中做了什么,getelementptr指令的语义很有趣。这是两行C源代码的结果:

ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
ev->handler(ev);

node属于ngx_rbtree_node_t类型,是ev类型ngx_event_t的成员。那就像:

struct ngx_event_t {
   ....
   struct ngx_rbtree_node_t time;
   ....
};

struct ngx_event_t *ev;
struct ngx_rbtree_node_t *node;

timerngx_event_t应指向的结构node成员的名称。

                 |<- ngx_rbtree_node_t ->|
|<-               ngx_event_t                      ->|
------------------------------------------------------
| (some data)    | "time"                | (some data)
------------------------------------------------------
^                ^
ev               node

上图显示了ngx_event_t实例的布局。 offsetof(ngx_event_t, time)的结果为40.这意味着some data之前的time为40个字节。而ngx_rbtree_node_t的大小也是40字节,巧合。因此,getelementptr指令的第一个索引oprand中的i64 -1计算包含ngx_event_t的{​​{1}}的基地址,该地址比node提前40个字节。

nodehandler的另一个成员,比ngx_event_t的基数落后16个字节。通过(另一)巧合,ngx_event_t的第三个成员也比ngx_rbtree_node_t的基址低16个字节。因此,getelementptr指令中的ngx_rbtree_node_t将向i32 2添加16个字节,以获取ev的地址。

请注意,16个字节是根据handler的布局计算的,而不是ngx_rbtree_node_t的布局。 Clang必须进行一些计算以确保getelementptr指令的正确性。在使用ngx_event_t的值之前,有一个bitcast指令将%handler强制转换为函数指针类型。

Clang做了什么打破了C源代码中定义的类型转换过程。但结果完全相同。