GCC-3.4.6源代码学习笔记(21)

3.3. 处理编译选项

3.3.1. 与优化有关的选项

回到decode_options,在480行,对于C++lang_hooks中的钩子initialize_diagnostics引用cxx_initialize_diagnostics。它设立诊断设施,用于给出足够详细、正确的出错消息。这里我们跳过它,因为它与真正的编译关系不大。

 

decode_options (continue)

 

489    /* Scan to see what optimization level has been specified. That will

490     determine the default value of many flags. */

491    for (i = 1; i < argc; i++)

492    {

493      if (!strcmp (argv[i], "-O"))

494      {

495        optimize = 1;

496        optimize_size = 0;

497      }

498      else if (argv[i][0] == '-' && argv[i][1] == 'O')

499      {

500        /* Handle -Os, -O2, -O3, -O69, ... */

501        const char *p = &argv[i][2];

502 

503        if ((p[0] == 's') && (p[1] == 0))

504        {

505          optimize_size = 1;

506 

507          /* Optimizing for size forces optimize to be 2. */

508          optimize = 2;

509        }

510        else

511        {

512          const int optimize_val = read_integral_parameter (p, p - 2, -1);

513          if (optimize_val != -1)

514          {

515            optimize = optimize_val;

516            optimize_size = 0;

517          }

518        }

519      }

520    }

 

GCC中,以下选项指示优化的级别[8]

-O:编译器尝试减少代码的大小和执行时间,但不会进行会使得调试变得困难的修改。这个选项打开选项-fno_optimize_size-fdefer_pop-fthread_jumps-jguess_branch_prob-cprop-registers-fdelayed_branch。而-fomit_frame_pointer的标识符,只有调试器可以不需要栈框指针也能工作时,才被设置。

-O0:默认值。关闭所有优化。设置-fno-merge-constants

-O1:与-O相同。

-O2:这个级别打开所有不涉及大小和速度权衡的优化。除了-O所打开的选项外,还包括-foptimize-sibling-calls-fcse-follow-jumps-fcse-skip-blocks-fgcse-fstrength-reduce-fexpensive-optimizations-frerun-loop-opt-fschedule-insns-fdelete-null-pointer-checks-fschedule-insn-after-reload-frerun-cse-after-loop-fpeephole2-fforce-mem-fcaller-saves-fstruct-aliasing-fregmove-freorder-blocks。这个级别不进行循环展开(loop unrolling),内联(inlining)和寄存器重命名(register renaming)。

-O3:除了-O2打开的选项,这个级别还打开-finline-functions-frename-registers

-Os:优化大小。设置所有-O2的选项标识符。同时-falign-loops-falign-jumps-falign-labels-falign-functions被设为1,防止因为对齐而插入间隙。

 

decode_options (continue)

 

522    if (!optimize)

523    {

524      flag_merge_constants = 0;

525    }

526 

527    if (optimize >= 1)

528    {

529      flag_defer_pop = 1;

530      flag_thread_jumps = 1;

531  #ifdef DELAY_SLOTS

532      flag_delayed_branch = 1;

533  #endif

534  #ifdef CAN_DEBUG_WITHOUT_FP

535      flag_omit_frame_pointer = 1;

536  #endif

537      flag_guess_branch_prob = 1;

538      flag_cprop_registers = 1;

539      flag_loop_optimize = 1;

540      flag_if_conversion = 1;

541      flag_if_conversion2 = 1;

542    }

543 

544    if (optimize >= 2)

545    {

546      flag_crossjumping = 1;

547      flag_optimize_sibling_calls = 1;

548      flag_cse_follow_jumps = 1;

549      flag_cse_skip_blocks = 1;

550      flag_gcse = 1;

551      flag_expensive_optimizations = 1;

552      flag_strength_reduce = 1;

553      flag_rerun_cse_after_loop = 1;

554      flag_rerun_loop_opt = 1;

555      flag_caller_saves = 1;

556      flag_force_mem = 1;

557      flag_peephole2 = 1;

558  #ifdef INSN_SCHEDULING

559      flag_schedule_insns = 1;

560      flag_schedule_insns_after_reload = 1;

561  #endif

562      flag_regmove = 1;

563      flag_strict_aliasing = 1;

564      flag_delete_null_pointer_checks = 1;

565      flag_reorder_blocks = 1;

566      flag_reorder_functions = 1;

567      flag_unit_at_a_time = 1;

568    }

569 

570    if (optimize >= 3)

571    {

572      flag_inline_functions = 1;

573      flag_rename_registers = 1;

574      flag_unswitch_loops = 1;

575      flag_web = 1;

576    }

577 

578    if (optimize < 2 || optimize_size)

579    {

580      align_loops = 1;

581      align_jumps = 1;

582      align_labels = 1;

583      align_functions = 1;

584 

585      /* Don't reorder blocks when optimizing for size because extra

586        jump insns may be created; also barrier may create extra padding.

587 

588        More correctly we should have a block reordering mode that tried

589        to minimize the combined size of all the jumps. This would more

590        or less automatically remove extra jumps, but would also try to

591        use more short jumps instead of long jumps. */

592      flag_reorder_blocks = 0;

593    }

 

上面的531行,宏DELAY_SLOTS由后端工具genattr,根据机器描述文件中是否出现define_delay模式来定义。而558行的INSN_SCHEDULING也是由genattr定义。

另外,这里用到了很多变量,我们首先看一下它们的意义。

flag_merge_constants-fmerge-constants-fmerge-all-constants),编译器尝试合并出现在多个常量段(constant section)的常量。如果值为1,只合并字符串常量和常量池中的常量,如果值为2,还要合并常量变量。

flag_defer_pop–fdef-pop),如果为非0值,在调用一个函数时压入栈的参数,在函数返回时不会立即弹出,而是允许积累多个函数调用的参数,而后把它们一起从栈中清除。

flag_thread_jumps-fthread-jumps),如果为非0值,当一个跳转的条件表达式的值,在跳转目的位置会导致另一个跳转发生时,那么原来的跳转会直接被重定向到最后的目的位置。

flag_omit_frame_pointer-fomit-frame-pointer),如果为非0值,当函数不需要栈框指针(frame pointer)时,则不把它保存在另一个寄存器里,也即省略保存和恢复的代码,同时增加一个可用的寄存器。对于所有的-O级别的优化,仅在调试器可以不需要栈框指针的情况下,这个标识符才会被设置。如果这个设置使得调试器不能运行(编译器将不设置这个标识符),一旦需要,你需要自己显式设置它。一些平台没有栈框指针,那么这个标识符没有作用。

flag_guess_branch_prob,如果为非0值,尝试猜测各分岔的可能性。

flag_cprop_registers在寄存器分配(register allocation)和寄存器分配后指令分裂(post-register allocation instruction splitting)后,执行一次拷贝-传播遍(copy-propagation pass)以尝试降低调度依赖性,偶尔可消除拷贝。

flag_rename_registers,如果为非0值,寄存器将被重命名。

flag_loop_optimize,如果为非0值,表示执行循环优化。

flag_if_conversion,如果为非0值,表示执行if转换。

flag_if_conversion2,如果为非0值,表示在重新加载(reload)后,执行if转换。

flag_crossjumping,如果为非0值,表示执行交叉跳转(crossjumping)。

flag_optimize_sibling_calls,如果为非0值,表示,允许GCC执行同属及尾递归调用优化(sibling and tail recursive call)。

flag_cse_follow_jumps-fcse-follow-jumps),如果为非0值,当跳转的目标只有这个跳转能到达,公共子表达式消除扫描(the common subexpression elimination scan)将跟随跳转的路径。也就是说,在跳转发生前任一值,在跳转的目的位置都存在,可在那里被使用。这个标识符被-O2-O3-Os所设置,但可以被-fno-cse-follow-jumps改写。

flag_cse_skip_blocks-fcse-skip-blocks),如果为非0值,当if语句足够简单,它不包含会影响之前已计算值的代码时,公共子表达式分析流(the common subexpression analysis flow)会跳过这个if语句并应用到跟随的语句上。这个标识符被-O2-O3-Os设置,但可以被-fno-cse-skip-blocks改写。

flag_gcse,如果为非0值,表示执行全局公共子表达式消除(CSE)。

flag_expensive_optimizations-fexpensive-optimizations),如果为非0值,开启一些有效但耗费编译时间的优化。例如,在全局公共子表达式消除后,再次运行公共子表达式消除。这个标识符设置时,其他一些优化会更深入地执行。这个标识符被-O2-O3-Os设置,但可以被-fno-expensive-optimizations改写。

flag_strength_reduce-fstrength-reduce),如果为非0值,执行循环强度折减(loop strength reduction)及消除内层循环使用的变量。这是一个把耗时的操作,例如乘法和除法,替换为更简单更快的操作,比如加和减的过程。这个标识符总是被-funroll_loops-funroll-all-loops设置。它也被-O2-O3-Os设置,但可以被-fno-strength-reduce改写。作为一个简单的例子,下面的循环使用了一个临时变量来保存计算出来的索引:

for(int i=0; i<10; i++) {

index = i * 2;

frammis(valarr[index]);

}

其内层的变量index可被消除,而且其中的乘法可以被变为简单的移位,从而得到:

for(int i=0; i<10; i++) {

frammis(valarr[i << 1]);

}

把循环计数左移一位即将其值加倍而且该值直接被用于数组的索引而不需要保存在临时变量中。

flag_rerun_cse_after_loop-frerun-cse-after-loop),如果为非0将导致在循环优化后再次应用公共子表达式优化。这样做是因为循环优化可能产生新的子表达式。这个标识符被-O2-O3-Os设置,但可以被-fno-rerun-cse-after-loop改写。这将增加编译时间大约20%并且辨认出更多的公共表达式。

flag_rerun_loop_opt-frerun-loop-opt),如果为非0值,运行2次循环优化。其中第二次不执行循环展开(loop unroll),但它会重新分析循环的指令。这个标识符被-O2-O3-Os设置,但可以被-fno-rerun-loop-opt改写。

flag_caller_saves-fcaller-saves),如果为非0值,在调用函数时包含额外的寄存器保存指令及退出时的恢复指令。这些寄存器可被用于该调用及函数中。只有存有有效值,而且只有保存与恢复看上去比需要时重载更好时,寄存器才会被保存。这个选项在某些机器上默认是打开的。它总是被-O2-O3-Os设置,但可以被-fno-caller-saves改写。

flag_force_mem-fforce-mem),如果为非0值,要进行数学计算的值必须要拷贝入寄存器。这会改进产生的代码,是因为需要的值通常已经载入寄存器,不需要重新载入。这个标识符被-O2-O3-Os设置。

flag_peephole2-fpeephole2),如果为非0值,在分配寄存器后,但在调度(scheduling)之前,启动RTL的窥孔优化(peephole optimization)。这个优化是一个机器特定的翻译,将一组指令翻译为另一组指令。这个选项是平台相关的,可能会没有作用。这个标识符被-O2-O3-Os设置,但可以被-fno-peephole2改写。

flag_schedule_insns-fscedule-insns),如果为非0值,在那些浮点或内存访问操作较慢的机器,或者那些可以在一个时钟周期执行多个指令的机器中,尝试改变指令顺序以消除空时钟周期(stalling)。在执行较慢指令的时间里,同时执行其他指令。这个标识符被-O2-O3-Os设置,但可被-fno-scedule-insns改写。

flag_schedule_insns_after_reload-fscedule-insns2),如果为非0值,除了是在为每个函数分配了全局及局部寄存器后执行,它和-fschedule_insns相同。这在有较少寄存器及寄存器加载比较慢的机器上有用。这个标识符被-O2-O3-Os设置,但可被-fno-scedule-insns2改写。

flag_regmove-foptimize-register-move-fregmove),如果为非0值,寄存器分配,通过改变移动数据操作中寄存器的赋值,来优化。这,在那些具有能直接将数据从内存一处移到另一处指令的机器上,尤其有效。这个标识符被-O2-O3-Os设置,但可被-fno-optimize-register-move改写。

flag_strict_aliasing-fstrict-aliasing),如果为非0值,根据正在编译的语言,采用对其而言最严格的别名规则(the strictest aliasing rule)。在使用严格别名的C程序中,例如,一个int不能是一个double或者指针的别名,但可以是一个unsigned int的别名。但对于union成员,即便使用最严格的别名规则,也不会有问题,只要访问是通过union本身,而不是通过指向成员地址的指针。下面的代码将产生问题:

int *iptr;

union {

int ivalue;

double dvalue;

} migs;

. . .

migs.ivalue = 45;

iptr = &migs.ivalue;

frammis(*iptr);

migs.dvalue = 88.6;

frammis(*iptr);

在这个例子中,严格别名分析可能不能发现iptr指向的值在2个函数调用间被改变了。然而通过union成员的访问,则不会产生这个问题。

flag_delete_null_pointer_checks-fdelete-null-pointer-checks),如果为非0值,当数据流分析(dataflow analysis)显示指针不可能为空时,移除检查解引用空指针的代码。在某些情况下,有可能会处理一个解引用的空指针,因此在那些依赖于这些空指针检查的程序中,不能使用这个选项。这个标识符被-O2-O3-Os设置,但可被-fno-delete-null-pointer-checks改写。

flag_reorder_blocks,如果为非0值,基本块(basic block)应被重排(reorder)。

flag_reorder_functions,如果为非0值,,函数应被重排。

flag_unit_at_a_time,如果为非0值,执行一次编译整个单元(whole unit at a time compilation)。

flag_inline_functions-finline-functions),如果为非0值,允许编译器在调用点,选择某些简单的函数,将其内联。同时如果该函数的声明方式使得所有对它的调用已知(例如,在C代码中的静态函数不能在文件外访问),那么该函数体将被忽略,因为它没有真正的调用。这个选项自动被-O3打开,除非指定了-fno-inline-functions

flag_unswitch_loops,如果为非0值,启动循环中去交替(loop unswitching)。

flag_web,如果为非0值,表示执行网构造遍(web construction pass)。

align_loops-falign-loop[=number]),对齐循环的顶部至等于或大于该number的边界,但只有不需要跳过多于number字节才这样做。例如,如果number20,得到的对齐量是32字节,只要这样做不需要跳过多于20字节。这个选项会使代码变大,因为需要插入伪代码来实现需要的对齐,但是,依赖于机器,循环可以运行得更快,因为每次迭代从循环底层跳回对齐的地址。如果number没有指定,则使用机器的默认值,通常是1。指定number1等同于-fno-align-loops,没有执行对齐。

align_jumps–falign-jumps[=number]),对齐跳转目的,而且这个地址只能由跳转到达,至等于或大于该number的边界,但只有不需要跳过多于number字节才这样做。例如,如果number20,得到的对齐量是32字节,只要这样做不需要跳过多于20字节。与类似的选项-falign-labels不同,这个选项不需要在跳转目的前插入伪代码。 this option does not require the insertion of dummy instructions before the branch target. 如果number没有指定,则使用机器的默认值,通常是1。指定number1等同于-fno-align-jumps,没有执行对齐。

align_labels–align-labels[=number]),对齐所有跳转的目的至等于或大于该number的边界,但只有不需要跳过多于number字节才这样做。例如,如果number20,得到的对齐量是32字节,只要这样做不需要跳过多于20字节。这个选项会使得代码变大变慢,因为需要在跳转目标前插入伪代码。至于类似,但更廉价的选项参见-falign-jumps。如果-falign-loops或者-falign-jumps使用了比number更大的值,这个更大的值也在此使用。 is used, with a greater value than number, the greater value is used here. If number is not specified, the machine default is used, which is normally 1. 如果number没有指定,则使用机器的默认值,通常是1。指定number1等同于-fno-align-labels,没有执行对齐。

align_functions–align-functions[=number]),对齐函数的起始地址至等于或大于该number的边界,但只有不需要跳过多于number字节才这样做。例如,如果number20,得到的对齐量是32字节,只要这样做不需要跳过多于20字节。设置number2的指数使得所有函数都对齐至这个边界。如果number没有指定,则使用机器的默认值,通常是1。指定number1等同于-fno-align-functions,没有执行对齐。

 

decode_options (continue)

 

595    /* Initialize whether `char' is signed. */

596    flag_signed_char = DEFAULT_SIGNED_CHAR;

597  #ifdef DEFAULT_SHORT_ENUMS

598    /* Initialize how much space enums occupy, by default. */

599    flag_short_enums = DEFAULT_SHORT_ENUMS;

600  #endif

601 

602    /* Initialize target_flags before OPTIMIZATION_OPTIONS so the latter can

603      modify it. */

604    target_flags = 0;

605    set_target_switch ("");

606 

607    /* Unwind tables are always present in an ABI-conformant IA-64

608      object file, so the default should be ON. */

609  #ifdef IA64_UNWIND_INFO

610    flag_unwind_tables = IA64_UNWIND_INFO;

611  #endif

612 

613  #ifdef OPTIMIZATION_OPTIONS

614    /* Allow default optimizations to be specified on a per-machine basis. */

615    OPTIMIZATION_OPTIONS (optimize, optimize_size);

616  #endif

 

596行,如果默认char是有符号的,DEFAULT_SIGNED_CHAR被定义为1,否则为0。而DEFAULT_SHORT_ENUMS只为DSP1600芯片定义。

 

你可能感兴趣的:(优化,branch,loops,optimization,Diagnostics,enums)