我在VC ++ 2010中编译了以下函数,以查看生成的优化(/ O2)32位代码(请注意switch语句的unsigned long long类型):
int proc1(unsigned long long a, char*s) {
switch (a ) {
case -1L :
printf("-1");
break;
case -11 :
printf("-11");
break;
case -3 :
printf("-3");
break;
case 21 :
printf("21");
break;
case 25 :
printf("25");
break;
case 29 :
printf("29");
break;
case 31 :
printf("31");
break;
case 40 :
printf("40");
break;
}
return strlen(s);
}
以下是生成代码的一部分:
_TEXT SEGMENT
_a$ = 8 ; size = 8
_s$ = 16 ; size = 4
?proc1@@YAH_KPAD@Z PROC ; proc1, COMDAT
; 5 : int proc1(unsigned long long a, char*s) {
push ebp
mov ebp, esp
; 6 :
; 7 : switch (a ) {
mov ecx, DWORD PTR _a$[ebp+4]
mov eax, DWORD PTR _a$[ebp]
test ecx, ecx
ja SHORT $LN13@proc1
jb SHORT $LN15@proc1
cmp eax, 40 ; 00000028H
ja SHORT $LN13@proc1
$LN15@proc1:
cmp eax, 40 ; 00000028H
jne SHORT $LN16@proc1
test ecx, ecx
je SHORT $LN1@proc1
$LN16@proc1:
test ecx, ecx
ja SHORT $LN14@proc1
jb SHORT $LN17@proc1
cmp eax, 29 ; 0000001dH
ja SHORT $LN14@proc1
但为什么在测试ecx,ecx之后它使用ja + jb?即:
test ecx, ecx
ja SHORT $LN13@proc1
jb SHORT $LN15@proc1
根据我的理解,test ecx,ecx
是测试ecx是否为零。因此,我希望在指令之后有一个je/jz/jnz
。 ja
应测试zf = 0 / cf = 1,而jb
应测试zf = 0 / cf = 0。因此,在test
指令之后使用它们是不合适的。而且jb
跟ja
之后甚至没有意义,因为两者都期望zf = 0并且cf在它们之前没有正确设置。
答案 0 :(得分:1)
JA
&使用JB
对是因为你在无符号类型上分支,并且由于寄存器跨越用于保存8字节值,这是测试负面情况的MSB(符号位)然后检查下面的DWORD积极的价值观。
在这里检查部分装配有点没用,需要检查整个功能以确定优化的范围。检查完整的装配,我们看到我们得到两个不同的部分,一个用于正面案例值,一个用于负面案例。
因为这个案例被转换成一组链式分支,我们在MSB的每个值上进行比较(跳到负部分),然后我们检查低DWORD以查看它是否大于最大值正值如果是,那么再次分支到负部分,最后我们与案例的值进行实际比较。
64TypeTe.p>/$ >PUSH EBP
00C21001 |. >MOV EBP,ESP
00C21003 |. >MOV ECX,DWORD PTR SS:[EBP+C]
00C21006 |. >MOV EAX,DWORD PTR SS:[EBP+8]
00C21009 |. >TEST ECX,ECX
00C2100B |. >JA 64TypeTe.00C210CD
00C21011 |. >JB SHORT 64TypeTe.00C2101C
00C21013 |. >CMP EAX,28
00C21016 |. >JA 64TypeTe.00C210CD
00C2101C |> >CMP EAX,28
00C2101F |. >JNZ SHORT 64TypeTe.00C21029
00C21021 |. >TEST ECX,ECX
00C21023 |. >JE 64TypeTe.00C210B8
00C21029 |> >TEST ECX,ECX
00C2102B |. >JA SHORT 64TypeTe.00C21096
00C2102D |. >JB SHORT 64TypeTe.00C21034
00C2102F |. >CMP EAX,1D
00C21032 |. >JA SHORT 64TypeTe.00C21096
00C21034 |> >CMP EAX,1D
00C21037 |. >JNZ SHORT 64TypeTe.00C2103D
00C21039 |. >TEST ECX,ECX
00C2103B |. >JE SHORT 64TypeTe.00C21081
00C2103D |> >CMP EAX,15
00C21040 |. >JNZ SHORT 64TypeTe.00C21046
00C21042 |. >TEST ECX,ECX
00C21044 |. >JE SHORT 64TypeTe.00C2106C
00C21046 |> >CMP EAX,19
00C21049 |. >JNZ 64TypeTe.00C21120
00C2104F |. >TEST ECX,ECX
00C21051 |. >JNZ 64TypeTe.00C21120
00C21057 |. >PUSH 64TypeTe.??_C@_02IFGANFKF@25?$AA@ ; /format = "25"
00C2105C |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C21062 |. >ADD ESP,4
00C21065 |. >MOV EAX,3
00C2106A |. >POP EBP
00C2106B |. >RETN
00C2106C |> >PUSH 64TypeTe.??_C@_02OBAMBAKB@21?$AA@ ; /format = "21"
00C21071 |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C21077 |. >ADD ESP,4
00C2107A |. >MOV EAX,3
00C2107F |. >POP EBP
00C21080 |. >RETN
00C21081 |> >PUSH 64TypeTe.??_C@_02CJNFJKKJ@29?$AA@ ; /format = "29"
00C21086 |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C2108C |. >ADD ESP,4
00C2108F |. >MOV EAX,3
00C21094 |. >POP EBP
00C21095 |. >RETN
00C21096 |> >CMP EAX,1F
00C21099 |. >JNZ 64TypeTe.00C21120
00C2109F |. >TEST ECX,ECX
00C210A1 |. >JNZ SHORT 64TypeTe.00C21120
00C210A3 |. >PUSH 64TypeTe.??_C@_02OAMOHKJG@31?$AA@ ; /format = "31"
00C210A8 |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C210AE |. >ADD ESP,4
00C210B1 |. >MOV EAX,3
00C210B6 |. >POP EBP
00C210B7 |. >RETN
00C210B8 |> >PUSH 64TypeTe.??_C@_02PMJKFNFC@40?$AA@ ; /format = "40"
00C210BD |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C210C3 |. >ADD ESP,4
00C210C6 |. >MOV EAX,3
00C210CB |. >POP EBP
00C210CC |. >RETN
00C210CD |> >CMP EAX,-0B
00C210D0 |. >JNZ SHORT 64TypeTe.00C210D7
00C210D2 |. >CMP ECX,-1
00C210D5 |. >JE SHORT 64TypeTe.00C21112
00C210D7 |> >CMP EAX,-3
00C210DA |. >JNZ SHORT 64TypeTe.00C210E1
00C210DC |. >CMP ECX,-1
00C210DF |. >JE SHORT 64TypeTe.00C210FD
00C210E1 |> >AND EAX,ECX
00C210E3 |. >CMP EAX,-1
00C210E6 |. >JNZ SHORT 64TypeTe.00C21120
00C210E8 |. >PUSH 64TypeTe.??_C@_02PGHGPEOM@?91?$AA@ ; /format = "-1"
00C210ED |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C210F3 |. >ADD ESP,4
00C210F6 |. >MOV EAX,3
00C210FB |. >POP EBP
00C210FC |. >RETN
00C210FD |> >PUSH 64TypeTe.??_C@_02MEEAJGGO@?93?$AA@ ; /format = "-3"
00C21102 |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C21108 |. >ADD ESP,4
00C2110B |. >MOV EAX,3
00C21110 |. >POP EBP
00C21111 |. >RETN
00C21112 |> >PUSH 64TypeTe.??_C@_03GPBHNPBF@?911?$AA@ ; /format = "-11"
00C21117 |. >CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
00C2111D |. >ADD ESP,4
00C21120 |> >MOV EAX,3
00C21125 |. >POP EBP
00C21126 \. >RETN
我实际上仍然会说这段代码是&#34;有问题&#34;由于对MSB和最大值的重复(多余)检查;这可能是由于当与链接分支结合时,对于寄存器跨越值的代码生成的疏忽(尽管它不太可能被修复,因为VS2010非常&#34;旧&#34;)。
在这种情况下,使用long long
似乎有点无意义(即使您在案例中签名了值,也使用无符号)。