Tchaikov’s Journal

March 15, 2006

calling convention 和 printf

Filed under: C++

前两天和同学谈到这个问题。同学说,Pascal convention 是无法实现类似printf 的变长参数的。虽然我也知道 C convention 和 Pascal convention 的意思和它们的区别,但是为什么后者无法实现 printf,这让我没有一点思路。
不管三七二十一,先补一下基础知识。Call Convention 是一套约定。它规定了调用方(caller)调用被调用的函数(callee)时,函数的参数应该以何种顺序传递给 callee,以及调用完成后,谁负责清理 stack frame。对了,为了简单起见,我们不讨论 C++ 的 inline 函数和成员函数。
通常使用的有三种 Calling Convention:C convention、Pascal convention 和stdcall convention。在 GCC 中,它们分别被记为:__cdecl、__pascal 和__stdcall。顾名思义,C convention 就是通常 C 语言所采用的约定。它要求参数传递的顺序为从右向左(即先把最右侧的参数压栈),同时由caller 负责清栈。Pascal convention 恰恰和 C convention 相反,参数从左至右,由 callee 负责清栈。
两个 Calling Convention 互有利弊。如果遵照 C convention,编译器每次看到有函数调用都会在调用处加入清理代码,每一处调用都会生成相应的二进制目标码,有100次调用的话就会有100份这样的清理代码(尽管有时调用的只是同一个函数)。这种效果如果累积的话,很可能会增加可执行文件的大小。倘若按照Pascal convention 行事,每个函数只在自己返回的地方加入清理代码,就算主程序调用了一百次子程序,可执行文件中的清理代码也只会有子程序的个数那么多份。如果大多调用的是相同的子程序,那么就会节省很多可执行文件的大小。在内存有限的情形,二进制文件的大小对程序的性能来说会是一个相当重要的因素。所以,Pascal convention 在很多情况能给程序稍稍减肥,从空间效率的角度来说,也提升了程序的性能。
但是,金无足赤,人无完人。Pascal convention 无法处理变长参数(variant length argument)的函数调用。在 C 里,printf 就是一个例子。若使用Pascal convention 的话,则要求 callee 在用毕输入参数后清栈(即调整%esp)。但是问题在于编译器在编译 printf 的时候根本就不知道它的参数到底有多少个!噢,你可能会说,“编译的时候源代码明明白白写在那里,编译器怎么会不知道呢?”。诚然,编译器在编译 caller 的代码时,它是清楚我们塞给printf 多少个参数的。但是 printf 作为 libc 的一员,一般是预编译好的库(precompiled library),在链接时才成为可执行文件的一部分。它,编译器,在那时(预编译时)是没有办法知道如何设置 %esp 的。这些信息只有在编译 caller 的源程序时,编译器才能知晓。
还有一个不是很充足的理由选择 C convention。假设我们使用汇编撰写子程序,call convention 使用 C。如果子程序的参数列表要添加一个参数(绝大多数情况是在后面添加)。为了访问这个参数,callee 只要用类似 (12)%ebp 的引用就可以了,对前面其它参数的引用不用更动。如果选了 Pascal convention 就麻烦了。对所有参数的引用都要往高地址移动 sizeof(last_param) 字节。另外,即使使用 C convention。虽然约定要求 caller 清栈,编译器也有自由不做这项工作,它可以重复使用参数在栈上占用的空间。
stdcall 是 win32 API 使用的 calling convention,它糅合了 C 和 Pascal两者的长处,参数从右到左传递,callee 负责清栈。不过因此,在 stdcall 下同样无法使用变长参数列表。由于访问寄存器比访问内存要快达数十倍,因此各家编译器也提供了(部分)使用寄存器传递参数的机制。在 MSVC 中,称之为 fastcall。GCC 则通过
“-mregparm=NNN”的参数来使用这个机制。用这种办法传递参数,对寄存器比较多的体系架构会有较大的性能改善。


http://gcc.gnu.org/onlinedocs/gnat_ugn_unw/Calling-Conventions.html
http://my.execpc.com/~geezer/osd/libc/index.htm#call

Comments »

The URI to TrackBack this entry is: http://tchaikov.blogsome.com/2006/03/15/p18/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Get free blog up and running in minutes with Blogsome
Theme designed by Jay of onefinejay.com