x86 - 为什么编译器会在下一条指令中插入看似毫无意义的JMP?

时间:2017-08-01 04:23:47

标签: c++ assembly x86

我正在尝试学习C ++和x86之间的关系,并且一直在查看某些C ++代码的相应汇编指令。引起我注意的是,下一条指令似乎有多个JMP。这不仅仅是在浪费空间和时钟周期吗?

源代码非常简单,只是来自教科书的简单游戏。

// Lost Fortune
// A personalized adventure!

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    const int GOLD_PIECES = 900;
    int adventurers, killed, survivors;
    string leader;

    // Get info.
    cout << "Welcome fo Lost Fortune\n\n";
    cout << "Please enter the following for your personalized adventure\n";

    cout << "Enter a number: ";
    cin >> adventurers;

    cout << "Enter a number, smaller than the first: ";
    cin >> killed;

    survivors = adventurers - killed;

    cout << "Enter your last name: ";
    cin >> leader;


    // Tell story.
    cout << "\nA brave group of " << adventurers << " set out on a quest ";
    cout << "-- in search of the lost treasure of the Ancient Dwarves. ";
    cout << "The group was led by that legendary rogue,  " << leader << ".\n";
    cout << "\n Along the way, a bang of marauding ogres ambushed the party. ";
    cout << "All fought bravely under the command of " << leader;
    cout << ", and the ogres were defeated, but at a cost. ";
    cout << "Of the adventurers, " << killed << " were vanquished, ";
    cout << "leaving just " << survivors << " in the group.\n";

    cout << "\nThe party was about to give up all hope. ";
    cout << "But while laying the deceased to rest, ";
    cout << "they stumbled upon the buried fortune. ";
    cout << "So the adventurers split " << GOLD_PIECES << " gold pieces. ";
    cout << leader << " held on to the extra " << (GOLD_PIECES % survivors);
    cout << " pieces to keep things fair of course.\n";

    return 0;
}

组装的摘录如下。请注意,有几个JMP转到下一条指令,地址为00002053,00002076等。如果有帮助,这些JMP的操作码为E9 00 00 00 00.

00002035         mov        eax, dword [ebp+var_64]                             ; CODE XREF=_main+148
00002038         lea        ecx, dword [eax-0x1f91+0x3c60]                      ; "Welcome fo Lost Fortune\\n\\n"
0000203e         mov        edx, esp
00002040         mov        dword [edx+4], ecx
00002043         mov        ecx, dword [eax-0x1f91+__ZNSt3__14coutE_400c]       ; __ZNSt3__14coutE_400c
00002049         mov        dword [edx], ecx
0000204b         call       imp___symbol_stub___ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc ; std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<< <std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*)
00002050         mov        dword [ebp+var_74], eax
00002053         jmp        loc_2058

             loc_2058:
00002058         mov        eax, dword [ebp+var_64]                             ; CODE XREF=_main+211
0000205b         lea        ecx, dword [eax-0x1f91+0x3c7a]                      ; "Please enter the following for your personalized adventure\\n"
00002061         mov        edx, esp
00002063         mov        dword [edx+4], ecx
00002066         mov        ecx, dword [eax-0x1f91+__ZNSt3__14coutE_400c]       ; __ZNSt3__14coutE_400c
0000206c         mov        dword [edx], ecx
0000206e         call       imp___symbol_stub___ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc ; std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<< <std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*)
00002073         mov        dword [ebp+var_78], eax
00002076         jmp        loc_207b

             loc_207b:
0000207b         mov        eax, dword [ebp+var_64]                             ; CODE XREF=_main+246
0000207e         lea        ecx, dword [eax-0x1f91+0x3cb6]                      ; "Enter a number: "
00002084         mov        edx, esp
00002086         mov        dword [edx+4], ecx
00002089         mov        ecx, dword [eax-0x1f91+__ZNSt3__14coutE_400c]       ; __ZNSt3__14coutE_400c
0000208f         mov        dword [edx], ecx
00002091         call       imp___symbol_stub___ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc ; std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<< <std::__1::char_traits<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, char const*)
00002096         mov        dword [ebp+var_7C], eax
00002099         jmp        loc_209e

             loc_209e:
0000209e         mov        eax, esp                                            ; CODE XREF=_main+281
000020a0         lea        ecx, dword [ebp+var_44]
000020a3         mov        dword [eax+4], ecx
000020a6         mov        ecx, dword [ebp+var_64]
000020a9         mov        edx, dword [ecx-0x1f91+__ZNSt3__13cinE_4008]        ; __ZNSt3__13cinE_4008
000020af         mov        dword [eax], edx
000020b1         call       imp___symbol_stub___ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi ; std::__1::basic_istream<char, std::__1::char_traits<char> >::operator>>(int&)
000020b6         mov        dword [ebp+var_80], eax
000020b9         jmp        loc_20be

为什么编译器会这样做?非常感谢任何帮助:)

3 个答案:

答案 0 :(得分:5)

在没有优化的情况下构建时,编译器的优先级是生成易于调试的代码,并尽可能地匹配原始源代码,而不是快速运行的代码。

在优化时(例如使用-O3),编译器的工作就是快速编写代码,并且不会像你观察到的那样使用JMP做愚蠢的事情。< / p>

优化的代码可能运行得更快,但调试起来非常困难。因此,如果您打算调试代码以查找错误,那么未经优化的版本几乎总是更可取。

答案 1 :(得分:0)

不需要那些跳转,它是特定于编译器的。 Visual Studio生成的汇编代码片段,作为调试版本。

library(dplyr)

getById <- function(id) {
  # create a connection and close it on exit
  conn <- DBI::dbConnect(
    drv = RMySQL::MySQL(),
    dbname = "shinydemo",
    host = "shiny-demo.csa7qlmguqrf.us-east-1.rds.amazonaws.com",
    username = "guest",
    password = "guest"
  )
  on.exit(DBI::dbDisconnect(conn))

  # get a specific row based on ID
  conn %>% tbl("City") %>% filter(ID == id) %>% collect()
}

parallel::mclapply(1:10, getById, mc.cores = 12)

调试期间看到的汇编代码:

; Line 19
        push    OFFSET ??_C@_0BK@OBIFJKE@Welcome?5fo?5Lost?5Fortune?6?6?$AA@
        mov     eax, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
        push    eax
        call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
        add     esp, 8
; Line 20
        push    OFFSET ??_C@_0DM@EEOOGOCI@Please?5enter?5the?5following?5for?5y@
        mov     eax, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
        push    eax
        call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
        add     esp, 8
; Line 22
        push    OFFSET ??_C@_0BB@ONCLGIPK@Enter?5a?5number?3?5?$AA@
        mov     eax, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
        push    eax
        call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char> >
        add     esp, 8
; Line 23

答案 2 :(得分:-1)

它们可能是为了帮助您在调试过程中单步执行,因为它们对应于源级别的每个语句。虽然,如果我是gcc,我宁愿插入int 3并稍后担心其他事情。