当CASE选择器长符号无符号时由VC ++生成奇怪的汇编

时间:2014-07-11 17:24:17

标签: visual-c++ assembly

我在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/jnzja应测试zf = 0 / cf = 1,而jb应测试zf = 0 / cf = 0。因此,在test指令之后使用它们是不合适的。而且jbja之后甚至没有意义,因为两者都期望zf = 0并且cf在它们之前没有正确设置。

1 个答案:

答案 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似乎有点无意义(即使您在案例中签名了值,也使用无符号)。