3.3.2. 初始化与目标平台相关选项
在上面605行, set_target_switch处理与目标平台相关的选项。所有这些选项都以“-m”开头。
3829 void
3830 set_target_switch (const char *name) in toplev.c
3831 {
3832 size_t j;
3833 int valid_target_option = 0;
3834
3835 for (j = 0; j < ARRAY_SIZE (target_switches); j++)
3836 if (!strcmp (target_switches [j].name, name))
3837 {
3838 if (target_switches [j].value < 0)
3839 target_flags &= ~target_switches[j].value;
3840 else
3841 target_flags |= target_switches [j].value;
3842 if (name[0] != 0)
3843 {
3844 if (target_switches [j].value < 0)
3845 target_flags_explicit |= -target_switches [j].value;
3846 else
3847 target_flags_explicit |= target_switches [j].value;
3848 }
3849 valid_target_option = 1;
3850 }
3851
3852 #ifdef TARGET_OPTIONS
3853 if (!valid_target_option)
3854 for (j = 0; j < ARRAY_SIZE (target_options); j++)
3855 {
3856 int len = strlen (target_options [j].prefix);
3857 if (target_options [j].value)
3858 {
3859 if (!strcmp (target_options [j].prefix, name))
3860 {
3861 *target_options [j].variable = target_options [j].value;
3862 valid_target_option = 1;
3863 }
3864 }
3865 else
3866 {
3867 if (!strncmp (target_options [j].prefix, name, len))
3868 {
3869 *target_options [j].variable = name + len;
3870 valid_target_option = 1;
3871 }
3872 }
3873 }
3874 #endif
3875
3876 if (!valid_target_option)
3877 error ("invalid option `%s'", name);
3878 }
上面第3835行,target_switches在toplev.c中定义如下。
1166 static const struct in toplev.c
1167 {
1168 const char *const name;
1169 const int value;
1170 const char *const description;
1171 }
1172 target_switches[] = TARGET_SWITCHES;
target_switches从TARGET_SWITCHES获取其内容,TARGET_SWITCHES是目标平台指定的,对于x86系统,与平台相关的选项显示如下,它们定义在i386.h文件中。
选项名
| 值 | 描述 |
80387 | MASK_80387 | 使用硬件fp |
no-80387 | -MASK_80387 | 不使用硬件fp |
hard-float | MASK_80387 | 使用硬件fp |
soft-float | -MASK_80387 | 不使用硬件fp |
no-soft-float | MASK_80387 | 使用硬件fp |
386 | 0 | "" /*Deprecated.*/ 已过时 |
486 | 0 | "" /*Deprecated.*/ 已过时 |
pentium | 0 | "" /*Deprecated.*/ 已过时 |
pentiumpro | 0 | "" /*Deprecated.*/ 已过时 |
pni | 0 | "" |
no-pni | 0 | "" /*Deprecated.*/ 已过时 |
intel-syntax | 0 | "" /*Deprecated.*/ 已过时 |
no-intel-syntax | 0 | "" /*Deprecated.*/ 已过时 |
rtd | MASK_RTD | 等同于stdcall |
no-rtd | -MASK_RTD | 使用普通的调用惯例 |
align-double | MASK_ALIGN_DOUBLE | 对齐某些double到dword边界 |
no-align-double | -MASK_ALIGN_DOUBLE | 对齐double到word边界 |
svr3-shlib | MASK_SVR3_SHLIB | 未初始化的局部变量置于.bss段 |
no-svr3-shlib | -MASK_SVR3_SHLIB | 未初始化的局部变量置于.data段 |
ieee-fp | MASK_IEEE_FP | 浮点比较使用IEEE标准 |
no-ieee-fp | -MASK_IEEE_FP | 浮点比较不使用IEEE标准 |
fp-ret-in-387 | MASK_FLOAT_RETURNS | 函数返回值置于FPU的寄存器 |
no-fp-ret-in-387 | -MASK_FLOAT_RETURNS | 不使用FPU的寄存器保存函数返回值 |
no-fancy-math-387 | MASK_NO_FANCY_MATH_387 | 不要为FPU产生sin,cos,sqrt指令 |
fancy-math-387 | -MASK_NO_FANCY_MATH_387 | 为FPU产生sin,cos,sqrt指令 |
omit-leaf-frame-pointer | MASK_OMIT_LEAF_FRAME_POINTER | 在叶子函数(即不在其中调用其他函数)中,忽略栈框指针 |
no-omit-leaf-frame-pointer | -MASK_OMIT_LEAF_FRAME_POINTER | "" |
stack-arg-probe | MASK_STACK_PROBE | 启动栈探测(stack probing) |
no-stack-arg-probe | -MASK_STACK_PROBE | "" |
windows | 0 | 0 /* undocumented */ |
dll | 0 | 0 /* undocumented */ |
align-stringops | -MASK_NO_ALIGN_STROPS | 对齐字符串操作的目的位置 |
no-align-stringops | MASK_NO_ALIGN_STROPS | 不对齐字符串操作的目的位置 |
inline-all-stringops | MASK_INLINE_ALL_STROPS | 内联所有已知字符串操作 |
no-inline-all-stringops | -MASK_INLINE_ALL_STROPS | 不内联所有已知字符串操作 |
push-args | -MASK_NO_PUSH_ARGS | 使用push指令保存溢出的参数(outgoing argument) |
no-push-args | MASK_NO_PUSH_ARGS | 不使用push指令保存溢出的参数(outgoing argument) |
accumulate-outgoing-args | MASK_ACCUMULATE_OUTGOING_ARGS | 使用push指令保存溢出的参数(outgoing argument) |
no-accumulate-outgoing-args | -MASK_ACCUMULATE_OUTGOING_ARGS | 不使用push指令保存溢出的参数(outgoing argument) |
mmx | MASK_MMX | 支持使用MMX的内建函数 |
no-mmx | -MASK_MMX | 不支持使用MMX的内建函数 |
3dnow | MASK_3DNOW | 支持使用3DNow!的内建函数 |
no-3dnow | -MASK_3DNOW | 不支持使用3DNow!的内建函数 |
sse | MASK_SSE | 支持使用MMX和SSE的内建函数,及MMX和SSE用于代码生成 |
no-sse | -MASK_SSE | 不支持使用MMX和SSE的内建函数,及MMX和SSE用于代码生成 |
sse2 | MASK_SSE2 | 支持使用MMX,SSE和SSE2的内建函数,及MMX,SSE和SSE2用于代码生成 |
no-sse2 | -MASK_SSE2 | 不支持使用MMX,SSE和SSE2的内建函数,及MMX,SSE和SSE2用于代码生成 |
sse3 | MASK_SSE3 | 支持使用MMX,SSE,SSE2和SSE3的内建函数,及MMX,SSE,SSE2和SSE3用于代码生成 |
no-sse3 | -MASK_SSE3 | 不支持使用MMX,SSE,SSE2和SSE3的内建函数,及MMX,SSE,SSE2和SSE3用于代码生成 |
128bit-long-double | MASK_128BIT_LONG_DOUBLE | sizeof(long double) 是16 |
96bit-long-double | -MASK_128BIT_LONG_DOUBLE | sizeof(long double) 是12 |
64 | MASK_64BIT | 产生64位x86-64代码 |
32 | -MASK_64BIT | 产生32位i386代码 |
ms-bitfields | MASK_MS_BITFIELD_LAYOUT | 使用原生的(MS)位域布局 |
no-ms-bitfields | -MASK_MS_BITFIELD_LAYOUT | 使用gcc默认的位域布局 |
red-zone | -MASK_NO_RED_ZONE | 在x86-64代码中使用红区(red-zone) |
no-red-zone | MASK_NO_RED_ZONE | 不在x86-64代码中使用红区(red-zone) |
tls-direct-seg-refs | MASK_TLS_DIRECT_SEG_REFS | 当访问tls数据时,直接引用%gs |
no-tls-direct-seg-refs | -MASK_TLS_DIRECT_SEG_REFS | 当访问tls数据时,不直接引用%gs |
默认选项 | TARGET_DEFAULT | TARGET_64BIT_DEFAULT | TARGET_SUBTARGET_DEFAULT| TARGET_TLS_DIRECT_SEG_REFS_DEFAULT |
表 9:用于i386系统的target_switches
上面在3839行,target_flags包含了位旗标来指示我们正在为之编译的机器的亚型,而target_flags_explicit是target_flags的一个掩模(mask),target_flags包含了X的比特,如果X出现在命令行上。
而在3854行,target_options也是与目标平台相关的选项,但这些选项必须被赋值。它的内容从TARGET_OPTIONS得来,而TARGET_OPTIONS在i386.h中定义。
1176 #ifdef TARGET_OPTIONS in toplev.c
1177 static const struct
1178 {
1179 const char *const prefix;
1180 const char **const variable;
1181 const char *const description;
1182 const char *const value;
1183 }
1184 target_options[] = TARGET_OPTIONS;
1185 #endif
前缀
| 变量 | 描述 | 值 |
tune= | &ix86_tune_string | 为指定的CPU调度指令 | 0 |
fpmath= | &ix86_fpmath_string | 使用给定的指令集产生浮点数学计算 | 0 |
arch= | &ix86_arch_string | 为指定的CPC产生代码 | 0 |
regparm= | &ix86_regparm_string | 用于传递参数的寄存器数目 | 0 |
align-loops= [obsolete] | &ix86_align_loops_string | 对齐循环代码至该值的2的指数倍边界 | 0 |
align-jumps=[obsolete] | &ix86_align_jumps_string | 跳转目的对齐至该值的2的指数倍边界 | 0 |
align-functions=[obsolete] | &ix86_align_funcs_string | 函数的起始地址对齐至该值的2的指数倍边界 | 0 |
preferred-stack-boundary= | x86_preferred_stack_boundary_string | 尽量把栈对齐在该值的2的指数倍边界 | 0 |
branch-cost= | &ix86_branch_cost_string | 跳转的代价(1-5,任意单位) | 0 |
cmodel= | &ix86_cmodel_string | 使用指定的x86-64代码模式 | 0 |
debug-arg | &ix86_debug_arg_string | "" /* Undocumented. */ | 0 |
asm= | &ix86_asm_string | 使用指定的汇编方言 | 0 |
tls-dialect= | &ix86_tls_dialect_string | 使用指定的线程局部储存方言 | 0 |
表 10:用于i386系统的target_options
注意到上表中的值域总是0,因此在set_target_switch,3865行的代码块总是得到执行,使对应的variable得到设置。
在decode_options的605行,当调用set_target_switch时,传入的参数是“”,这等同于0,从而从TARGET_SWITCHES获得值。
在设置了选项的默认值后,在615行, OPTIMIZATION_OPTIONS根据指定的目标平台调整这些默认值。对于x86系统,这个宏及相关函数有如下定义。
512 #define OPTIMIZATION_OPTIONS(LEVEL, SIZE) / in i386.h
513 optimization_options ((LEVEL), (SIZE))
1502 void
1503 optimization_options (int level, int size ATTRIBUTE_UNUSED) in i386.c
1504 {
1505 /* For -O2 and beyond, turn off -fschedule-insns by default. It tends to
1506 make the problem with not enough registers even worse. */
1507 #ifdef INSN_SCHEDULING
1508 if (level > 1)
1509 flag_schedule_insns = 0;
1510 #endif
1511
1512 /* The default values of these switches depend on the TARGET_64BIT
1513 that is not known at this moment. Mark these values with 2 and
1514 let user the to override these. In case there is no command line option
1515 specifying them, we will set the defaults in override_options. */
1516 if (optimize >= 1)
1517 flag_omit_frame_pointer = 2;
1518 flag_pcc_struct_return = 2;
1519 flag_asynchronous_unwind_tables = 2;
1520 }
上面,flag_schedule_insn和flag_omit_frame_pointer 之前已碰到。而其他,其细节如下。
flag_pcc_struct_return(-fpcc-struct-return),如果为非0值,生成代码,把所有的结构体存放在内存中来返回,即便它们小得可以放进寄存器。通过寄存器或内存返回结构体的确切的协定(precise convention),依赖于目标平台。虽然产生效率受损的代码,但在与由别的编译器生成的目标文件链接时,可能需要这个选项。
同样(-freg-struct-return),如果为非0值,生成代码,把小的结构体放入寄存器返回。如果它们不是小到可以放入寄存器,则通过内存返回。通过寄存器或内存返回结构体的确切的协定(precise convention),依赖于目标平台。
flag_asynchronous_unwind_tables(-fasynchronous-unwind-tables),如果为非0值,当目标机器支持时,生成DWARF2格式的回退表(unwind table)。表精切记录了每个指令的边界,因此它可以在异步事件(asynchronous events,例如调试器或垃圾收集器)中,用于回退栈(stack unwinding)。