随笔-341  评论-2670  文章-0  trackbacks-0
    stdcall、cdecl和fastcall的参数都是从右到左入栈,并且返回值遵循以下规律:
    小于等于4字节结构用EAX
    小于等于8字节结构用EDX:EAX
    浮点数用ST(0)
    其他则在EAX放置一个指针,供返回值使用

    stdcall被调用者清栈,cdecl调用者清栈,fastcall被调用者清栈并且前两个小于等于4字节的参数放入ECX和EDX。返回值和参数如果一方有构造函数或析构函数则不使用寄存器。

    于是今天用字符串形式的汇编写了三种调用方法的求和函数,类型如下:
1 typedef VInt (__stdcall * Summer_Stdcall)(VInt* Numbers , VInt Count);
2 typedef VInt (__cdecl * Summer_Cdecl)(VInt* Numbers , VInt Count);
3 typedef VInt (__fastcall * Summer_Fastcall)(VInt* Numbers , VInt Count);

    汇编代码如下:
 1 CONSTANT
 2 VARIABLE
 3 CODE
 4 
 5 @SUM_STDCALL:
 6   PUSH EBP
 7   MOV EBP, ESP
 8   PUSH ECX
 9   PUSH EDI
10   XOR EAX, EAX
11   MOV ECX, int32 [EBP+12]
12   MOV EDI, int32 [EBP+8]
13 @SUM_STDCALL_BEGIN:
14   CMP ECX, int32 0
15   JE @SUM_STDCALL_FINISHED
16   ADD EAX, int32 [EDI]
17   ADD EDI, int32 4
18   DEC ECX
19   JMP @SUM_STDCALL_BEGIN
20 @SUM_STDCALL_FINISHED:
21   POP EDI
22   POP ECX
23   MOV ESP, EBP
24   POP EBP
25   RET int16 8
26 
27 @SUM_CDECL:
28   PUSH EBP
29   MOV EBP, ESP
30   PUSH ECX
31   PUSH EDI
32   XOR EAX, EAX
33   MOV ECX, int32 [EBP+12]
34   MOV EDI, int32 [EBP+8]
35 @SUM_CDECL_BEGIN:
36   CMP ECX, int32 0
37   JE @SUM_CDECL_FINISHED
38   ADD EAX, int32 [EDI]
39   ADD EDI, int32 4
40   DEC ECX
41   JMP @SUM_CDECL_BEGIN
42 @SUM_CDECL_FINISHED:
43   POP EDI
44   POP ECX
45   MOV ESP, EBP
46   POP EBP
47   RET
48 
49 @SUM_FASTCALL:
50   PUSH EBP
51   MOV EBP, ESP
52   XOR EAX, EAX
53 @SUM_FASTCALL_BEGIN:
54   CMP EDX, int32 0
55   JE @SUM_FASTCALL_FINISHED
56   ADD EAX, int32 [ECX]
57   ADD ECX, int32 4
58   DEC EDX
59   JMP @SUM_FASTCALL_BEGIN
60 @SUM_FASTCALL_FINISHED:
61   MOV ESP, EBP
62   POP EBP
63   RET

    使用以下方法读取文件、编译并取出三个label的指针:
 1 void RunExecutable(VL_AsmProgram* Program , VL_AsmCompiled* Compiled , VL_AsmExecutable* Executable)
 2 {
 3     VInt Numbers[]={11,12,13,14,15,16,17,18,19,20};
 4     VInt Count=sizeof(Numbers)/sizeof(*Numbers);
 5     {
 6         VInt Offset=(VInt)Compiled->LabelOffsets[Program->LabelNames.IndexOf(L"@SUM_STDCALL")];
 7         Summer_Stdcall Summer=(Summer_Stdcall)((VInt)Executable->GetInstruction()+Offset);
 8         VInt Result=Summer(Numbers,Count);
 9         GetConsole()->Write(L"结果:"+VUnicodeString(Result)+L"\r\n");
10     }
11     {
12         VInt Offset=(VInt)Compiled->LabelOffsets[Program->LabelNames.IndexOf(L"@SUM_CDECL")];
13         Summer_Cdecl Summer=(Summer_Cdecl)((VInt)Executable->GetInstruction()+Offset);
14         VInt Result=Summer(Numbers,Count);
15         GetConsole()->Write(L"结果:"+VUnicodeString(Result)+L"\r\n");
16     }
17     {
18         VInt Offset=(VInt)Compiled->LabelOffsets[Program->LabelNames.IndexOf(L"@SUM_FASTCALL")];
19         Summer_Fastcall Summer=(Summer_Fastcall)((VInt)Executable->GetInstruction()+Offset);
20         VInt Result=Summer(Numbers,Count);
21         GetConsole()->Write(L"结果:"+VUnicodeString(Result)+L"\r\n");
22     }
23 }
24 
25 void Main_Assembler()
26 {
27     VUnicodeString Code;
28     VInt Line=0;
29     VUnicodeString Message;
30     {
31         VUnicodeString WorkData=VFileName(GetConsole()->GetAppPath()).MakeAbsolute(L"..\\..\\TestData\\").GetStrW();
32         VL_FileStream CodeStream(WorkData+L"Assembly.txt",VL_FileStream::vomRead);
33         Code=ReadText(&CodeStream);
34     }
35 
36     VL_AsmProgram* Program=CompileToAssembly(Code,Line,Message);
37     if(!Program)
38     {
39         GetConsole()->Write(Message);
40         return;
41     }
42 
43     VL_AsmCompiled* Compiled=CompileToX86(Program);
44     if(Compiled->Errors.GetCount())
45     {
46         PrintErrors(Program,Compiled);
47         delete Program;
48         delete Compiled;
49         return;
50     }
51 
52     VL_AsmExecutable* Executable=LinkX86(Compiled);
53     if(Compiled->Errors.GetCount())
54     {
55         PrintErrors(Program,Compiled);
56     }
57     if(Executable)
58     {
59         RunExecutable(Program,Compiled,Executable);
60         delete Executable;
61     }
62     delete Program;
63     delete Compiled;
64 }

    得到结果:

    接下来熟悉浮点数的操作,就可以开始中间指令集的构造了。
posted on 2009-03-01 05:27 陈梓瀚(vczh) 阅读(1772) 评论(9)  编辑 收藏 引用 所属分类: JIT

评论:
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-03 05:39 | lnn
To day is my birthday!  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-03 21:20 | OwnWaterloo
--------------------------------------------------------------------------------------------
stdcall、cdecl和fastcall的参数都是从右到左入栈,并且返回值遵循以下规律:
    小于等于4字节结构用EAX
    小于等于8字节结构用EDX:EAX
    浮点数用ST(0)
    其他则在EAX放置一个指针,供返回值使用
--------------------------------------------------------------------------------------------
 
请问一下,关于这个规律,是有标准规定的吗?
C/C++的书籍或者标准中,都没有规定调用约定。
还是说,这些只是事实上的标准?
 
如果只是事实上的标准,这些规律的应用范围只限于x86以及其兼容机?
  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-04 09:27 | 路人戊
@OwnWaterloo
参数入栈方向没有标准,甚至没有事实上的标准,IA86上GCC和VC编译器方向就不同。
其他寄存器多的CPU编译器倾向尽可能地利用寄存器来传递参数,会出现调用函数压栈就只是压进一个返回地址的情形

一击脱离……  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-04 19:20 | 陈梓瀚(vczh)
@路人戊
这是windows下编译器的约定,windows下的vc++、gcc和c++builder都遵守这个约定。至于其他的怎么办其实都无所谓了,因为windows的程序只能直接调用windows的程序。  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-04 19:21 | 陈梓瀚(vczh)
@OwnWaterloo
这在msdn里有详细描述。  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-04 23:44 | OwnWaterloo
@陈梓瀚(vczh)
MSDN就是微软的编译器了。

其他的编译器是否也是这样?

虽然我也觉得其他编译器也应该是这样。
没有理由无故的与楼主描述的__cdecl,__stdcall实现不兼容。

但是心里没谱啊 ……
楼主作过其他编译器的调查么?
有前人作过类似的调查么?
  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-05 00:07 | 陈梓瀚(vczh)
@OwnWaterloo
我以前用的是C++Builder,根据观察一致。而且这里有一个很强大的理由,如果不兼容的话,怎么调API?所以必须兼容。  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-05 03:03 | OwnWaterloo
@陈梓瀚(vczh)
---------------------------------------------
如果不兼容的话,怎么调API?所以必须兼容。
---------------------------------------------
---------------------------------------------
虽然我也觉得其他编译器也应该是这样。
没有理由无故的与楼主描述的__cdecl,__stdcall实现不兼容。
---------------------------------------------
我想说的就是这个意思,应该不会有编译器放弃大多数旧有的C目标文件遵守的约定,另寻它法。


但是呢。。。 还是不像有个ISO那么有把握。


http://www.unixwiz.net/techtips/win32-callconv.html
这篇文章里说了一个入栈顺序,和平时所说的右到左不同。
但在win32上结论是一样的。

  回复  更多评论
  
# re: JIT脚本引擎:stdcall、cdecl和fastcall 2009-03-05 04:45 | 陈梓瀚(vczh)
@OwnWaterloo
有了ISO也不见得就怎么样  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理