c
中的函数:
PHPAPI char *php_pcre_replace(char *regex, int regex_len,
char *subject, int subject_len,
zval *replace_val, int is_callable_replace,
int *result_len, int limit, int *replace_count TSRMLS_DC)
{
pcre_cache_entry *pce; /* Compiled regular expression */
/* Compile regex or get it from cache. */
if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
return NULL;
}
....
}
它的组装:
php5ts!php_pcre_replace:
1015db70 8b442408 mov eax,dword ptr [esp+8]
1015db74 8b4c2404 mov ecx,dword ptr [esp+4]
1015db78 56 push esi
1015db79 8b74242c mov esi,dword ptr [esp+2Ch]
1015db7d 56 push esi
1015db7e 50 push eax
1015db7f 51 push ecx
1015db80 e8cbeaffff call php5ts!pcre_get_compiled_regex_cache (1015c650)
1015db85 83c40c add esp,0Ch
1015db88 85c0 test eax,eax
1015db8a 7502 jne php5ts!php_pcre_replace+0x1e (1015db8e)
php5ts!php_pcre_replace+0x1c:
1015db8c 5e pop esi
1015db8d c3 ret
c函数调用pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)
对应1015db7d~1015db80
将3个参数入栈调用。
但我的疑问是,在这么多的寄存器中,编译器是如何决定使用eax
、ecx
和esi
的(这是特殊的,因为它在使用前已经恢复了,为什么?)作为携带到堆栈的中间体?
c 中一定有一些隐藏的指示告诉编译器这样做,对吧?
最佳答案
不,没有隐藏的指示。
这是生成 80x86 指令的典型策略,被许多编译器实现使用,C
和其他。例如,1980 年代的英特尔 Fortran-77 编译器在启用优化时会做同样的事情。
优先使用 eax
和 ecx
可能是避免使用 esi
和 edi
的产物,因为这些寄存器不能直接用于加载字节操作数。
为什么不是 ebx
和 edx
?好吧,许多代码生成器更喜欢在评估复杂结构评估时保存中间指针,也就是说,根本没有太多理由。编译器只是寻找两个可用的寄存器来使用并覆盖它们以缓冲值。
为什么不像这样重用 eax
呢?:
push esi
mov eax,dword ptr [esp+2Ch]
push eax
mov eax,dword ptr [esp+8]
push eax
mov eax,dword ptr [esp+4]
push eax
因为这会导致流水线停止等待 eax
完成之前的内存周期,自 80586 以来的 80x86 秒(可能是 80486——太久远了,我无法确定)。
x86 架构是一个奇怪的野兽。每个寄存器,虽然被 Intel 提升为“通用”,但都有其怪癖(例如,cx
/ecx
与 loop
指令相关联,并且 eax:edx
与乘法指令相关联)。这与优化执行以避免缓存未命中和管道停顿的特殊方法相结合,通常会导致代码生成器生成难以理解的代码,而代码生成器会将所有这些因素都考虑在内。
关于c - 深入 assembly ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6079543/