使用两个函数指针key_eq
和key_hash
实现数据结构。当这些函数指针只设置一次并且从未修改过时,我希望gcc直接在函数地址中编译而不是加载struct成员,但是单个puts("")
调用会破坏gcc的常量传播(正确的术语?)
以下是ppc asm的简短片段。可以看到存储了字段key_hash
,我们称之为libc puts
,然后加载key_hash
。如果删除puts
,则会正确传播常量。这是不可避免的吗?
lis 9,hasheq_string@ha # tmp174,
la 0,hasheq_string@l(9) # tmp173,, tmp174
stw 3,16(1) # h.flags,
lis 9,hashf_string@ha # tmp176,
lis 3,.LC0@ha # tmp178,
stw 0,36(1) # h.key_eq, tmp173
la 0,hashf_string@l(9) # tmp175,, tmp176
la 3,.LC0@l(3) #,, tmp178
stw 0,40(1) # h.key_hash, tmp175
bl puts #
lwz 0,40(1) # h.key_hash, h.key_hash
mr 3,31 #, tmp164
stw 31,32(1) # h._key, tmp164
mtctr 0 #, h.key_hash
bctrl #
我很抱歉没有使用C测试用程序,我很难再现它,但短三行“存储,bl put,load”序列引出了问题,这是不可能的围绕?
答案 0 :(得分:1)
GCC可能保守地假设对puts
的调用可能会读/写内存,因此调用函数可能需要key_hash
中的值,并且在调用之前和之后不同。
此代码:
struct S
{
int f;
};
void bar ();
int
foo (struct S *s)
{
s->f = 314;
bar ();
return s->f + 2;
}
产生“签名”序列:
foo:
mflr 0
stwu 1,-32(1)
stw 29,20(1)
mr 29,3
stw 0,36(1)
li 0,314
stw 0,0(3) ;;
bl bar ;; here we go
lwz 3,0(29) ;;
lwz 0,36(1)
addi 3,3,2
lwz 29,20(1)
mtlr 0
addi 1,1,32
blr
但是,通过一个小的修改(手动标量替换聚合?):
struct S
{
int f;
};
void bar ();
int
foo (struct S *s)
{
int i;
s->f = i = 314;
bar ();
return i + 2;
}
获得了预期的结果:
foo:
mflr 0
stwu 1,-16(1)
stw 0,20(1)
li 0,314
stw 0,0(3)
bl bar
lwz 0,20(1)
li 3,316 ;; constant propagated and folded
addi 1,1,16
mtlr 0
blr