GCC-3.4.6源代码学习笔记(26续1)

common_handle_option (continue)

 

909      case OPT_fcall_used_:

910        fix_register (arg, 0, 1);

911        break;

912 

913      case OPT_fcall_saved_:

914        fix_register (arg, 0, 0);

915        break;

916 

917      case OPT_fcaller_saves:

918        flag_caller_saves = value;

919        break;

920 

921      case OPT_fcommon:

922        flag_no_common = !value;

923        break;

924 

925      case OPT_fcprop_registers:

926        flag_cprop_registers = value;

927        break;

928 

929      case OPT_fcrossjumping:

930        flag_crossjumping = value;

931        break;

932 

933      case OPT_fcse_follow_jumps:

934       flag_cse_follow_jumps = value;

935        break;

936 

937      case OPT_fcse_skip_blocks:

938        flag_cse_skip_blocks = value;

939        break;

940 

941      case OPT_fdata_sections:

942        flag_data_sections = value;

943        break;

944 

945      case OPT_fdefer_pop:

946        flag_defer_pop = value;

947        break;

948 

949      case OPT_fdelayed_branch:

950        flag_delayed_branch = value;

951        break;

952 

953      case OPT_fdelete_null_pointer_checks:

954        flag_delete_null_pointer_checks = value;

955        break;

 

选项–fcall-used-`register`使具名寄存器可用于寄存器分配,但其内容可被函数调用所破坏。它可被分配用于临时存储,但必须在函数调用后重新加载。这个选项不可用于有固定用途的寄存器(例如栈框指针或栈指针)。寄存器的名字依赖于目标平台,由机器描述中的REGISTER_NAMES宏来命名。

而选项–fcaller-saved-`register`使具名寄存器可用于保存值,而且其内容不被函数调用破坏。以此选项设置编译的函数必须保持及恢复该寄存器的内容。这个选项不可用于有固定用途的寄存器(例如栈框指针或栈指针)。寄存器的名字依赖于目标平台,由机器描述中的REGISTER_NAMES宏来命名。

2个选项均由fix_register处理。注意传递给函数的call_used参数。

 

720  void

721  fix_register (const char *name, int fixed, int call_used)                                         in regclass.c

722  {

723    int i;

724 

725    /* Decode the name and update the primary form of

726      the register info.  */

727 

728    if ((i = decode_reg_name (name)) >= 0)

729    {

730      if ((i == STACK_POINTER_REGNUM

731  #ifdef HARD_FRAME_POINTER_REGNUM

732         || i == HARD_FRAME_POINTER_REGNUM

733  #else

734         || i == FRAME_POINTER_REGNUM

735  #endif

736         )

737         && (fixed == 0 || call_used == 0))

738      {

739        static const char * const what_option[2][2] = {

740            { "call-saved", "call-used" },

741            { "no-such-option", "fixed" }};

742 

743        error ("can't use '%s' as a %s register", name,

744             what_option[fixed][call_used]);

745      }

746      else

747      {

748        fixed_regs[i] = fixed;

749        call_used_regs[i] = call_used;

750  #ifdef CALL_REALLY_USED_REGISTERS

751        if (fixed == 0)

752          call_really_used_regs[i] = call_used;

753  #endif

754      }

755    }

756    else

757    {

758      warning ("unknown register name: %s", name);

759    }

760  }

 

decode_reg_name用于获取寄存器名。对于汇编语言,类如Linux所使用的,使用前缀%来引用寄存器,在下面的681行,strip_reg_name除去这个前缀。注意到,我们可以使用十进制数字或名字来选择寄存器。对于名字,它由reg_names 来匹配。reg_names包含由REGISTER_NAMES指定的寄存器名。

 

673  int

674  decode_reg_name (const char *asmspec)                                                       in varasm.c

675  {

676    if (asmspec != 0)

677    {

678      int i;

679 

680      /* Get rid of confusing prefixes.  */

681      asmspec = strip_reg_name (asmspec);

682 

683      /* Allow a decimal number as a "register name".  */

684      for (i = strlen (asmspec) - 1; i >= 0; i--)

685        if (! ISDIGIT (asmspec[i]))

686          break;

687      if (asmspec[0] != 0 && i < 0)

688      {

689        i = atoi (asmspec);

690        if (i < FIRST_PSEUDO_REGISTER && i >= 0)

691          return i;

692        else

693          return -2;

694      }

695 

696      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

697        if (reg_names[i][0]

698           && ! strcmp (asmspec, strip_reg_name (reg_names [i])))

699          return i;

700 

701  #ifdef ADDITIONAL_REGISTER_NAMES

702      {

703        static const struct { const char *const name; const int number; } table[]

704             = ADDITIONAL_REGISTER_NAMES;

705 

706        for (i = 0; i < (int) ARRAY_SIZE (table); i++)

707          if (! strcmp (asmspec, table[i].name))

708            return table[i].number;

709      }

710  #endif /* ADDITIONAL_REGISTER_NAMES */

711 

712      if (!strcmp (asmspec, "memory"))

713        return -4;

714 

715      if (!strcmp (asmspec, "cc"))

716        return -3;

717 

718      return -2;

719    }

720 

721    return -1;

722  }

 

对于x86机器,REGISTER_NAMES定义了基本寄存器集。这部分寄存器可由编译器的真实寄存器号(hard-register-number)索引。

 

2746 #define HI_REGISTER_NAMES                            /                                         in i386.h

2747 {"ax","dx","cx","bx","si","di","bp","sp",                        /

2748  "st","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)",        /

2749  "argp", "flags", "fpsr", "dirflag", "frame",                           /

2750  "xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7",            /

2751  "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"      ,             /

2752  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",                     /

2753  "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}

2754

2755 #define REGISTER_NAMES HI_REGISTER_NAMES

 

更者对于x86机器ADDITIONAL_REGISTER_NAMES定义了平台中额外的寄存器但这部分寄存器不能由编译器的真实寄存器号hard-register-number索引。

 

2759 #define ADDITIONAL_REGISTER_NAMES /                                            in i386.h

2760 { { "eax", 0 }, { "edx", 1 }, { "ecx", 2 }, { "ebx", 3 },     /

2761   { "esi", 4 }, { "edi", 5 }, { "ebp", 6 }, { "esp", 7 },     /

2762   { "rax", 0 }, { "rdx", 1 }, { "rcx", 2 }, { "rbx", 3 },    /

2763   { "rsi", 4 }, { "rdi", 5 }, { "rbp", 6 }, { "rsp", 7 },      /

2764   { "al", 0 }, { "dl", 1 }, { "cl", 2 }, { "bl", 3 },            /

2765   { "ah", 0 }, { "dh", 1 }, { "ch", 2 }, { "bh", 3 },         /

2766   { "mm0", 8},  { "mm1", 9},  { "mm2", 10}, { "mm3", 11},    /

2767   { "mm4", 12}, { "mm5", 13}, { "mm6", 14}, { "mm7", 15} }

 

因此在fix_register748行,fixed_regs由真实寄存器号索引,对于有固定用途的寄存器,相应的比特位为1。这些寄存器不可用于分配通用伪寄存器。

749行,call_used_regs亦由真实寄存器号索引,对于有固定用途或能被函数调用破坏的寄存器,相应的比特位为1。这些寄存器不可用于分配生命周期跨越多个函数调用的通用伪寄存器,除非在函数调用间对其进行保存/恢复操作。

750行,宏CALL_REALLY_USED_REGISTERS在我们的目标平台下没有定义。call_really_used_regs亦没有被声明。

回到common_handle_option,在922行,flag_no_common-fcommon),指明-fno-common 将使得编译器为每个全局变量,在数据段显式地分配空间。否则则是将它们分配于公共块,由链接器来解析(resolve),因此多次声明同一个全局变量将导致链接器把它们解析为1个。可以指定-fno-common来确认程序可以在其他不使用GCC的系统上编译链接。

942行,flag_data_sections-fdata-sections)如果非0,在汇编语言输出中,每个数据项被置于其自命名的段中。该段名由数据项名导出。这,仅在具有能使用分段优化空间分配链接器的机器上,带来好处。对于可执行代码的相同优化参见-ffunction-sections。对在其汇编代码中不支持分段的机器,这个选项将导致警告,并被忽略。即便在那些支持的机器上,除非链接器将其用于优化,也不会带来好处。事实上,这可能带来不利的影响,使得目标代码更大,加载更慢。

如果选项-p为概要分析(profiling)设置,这个选项(-fdata-sections)将无效。另外,因为代码重排(rearrangement of the code)的原因,使用-g选项及程序调试可能会有问题。

950行,flag_delayed_branch-fdelayed-branch)如果非0,这个选项仅在具有延迟跳转时隙(delayed branch slots)的机器上起作用。这涉及在决定跳转的分支的同时,加载并执行指令。在作出决定后,指令的结果可能被抛弃,取决于指令的位置及所作的决定。如果目标机器支持,这个标识将被各级优化所设置。它亦可被-fno-delayed-branch改写。

 

common_handle_option (continue)

 

957       case OPT_fdiagnostics_show_location_:

958         if (!strcmp (arg, "once"))

959           diagnostic_prefixing_rule (global_dc) = DIAGNOSTICS_SHOW_PREFIX_ONCE;

960         else if (!strcmp (arg, "every-line"))

961           diagnostic_prefixing_rule (global_dc)

962               = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;

963         else

964           return 0;

965         break;

966 

967       case OPT_fdump_unnumbered:

968         flag_dump_unnumbered = value;

969         break;

970 

971       case OPT_feliminate_dwarf2_dups:

972         flag_eliminate_dwarf2_dups = value;

973         break;

974 

975       case OPT_feliminate_unused_debug_types:

976         flag_eliminate_unused_debug_types = value;

977         break;

978 

979         case OPT_feliminate_unused_debug_symbols:

980           flag_debug_only_used_symbols = value;

981           break;

982       

983         case OPT_fexceptions:

984           flag_exceptions = value;

985           break;

986       

987         case OPT_fexpensive_optimizations:

988           flag_expensive_optimizations = value;

989           break;

990       

991         case OPT_ffast_math:

992           set_fast_math_flags (value);

993           break;

994       

995         case OPT_ffinite_math_only:

996           flag_finite_math_only = value;

997           break;

998       

999      case OPT_ffixed_:

1000         fix_register (arg, 1, 1);

1001       break;

1002

1003     case OPT_ffunction_cse:

1004       flag_no_function_cse = !value;

1005       break;

1006

1007     case OPT_ffloat_store:

1008       flag_float_store = value;

1009       break;

1010

1011     case OPT_fforce_addr:

1012       flag_force_addr = value;

1013       break;

1014

1015     case OPT_fforce_mem:

1016       flag_force_mem = value;

1017       break;

1018

1019     case OPT_ffunction_sections:

1020       flag_function_sections = value;

1021       break;

1022

1023     case OPT_fgcse:

1024       flag_gcse = value;

1025       break;

1026

1027     case OPT_fgcse_lm:

1028       flag_gcse_lm = value;

1029       break;

1030

1031     case OPT_fgcse_sm:

1032       flag_gcse_sm = value;

1033       break;

1034

1035     case OPT_fgcse_las:

1036       flag_gcse_las = value;

1037       break;

1038

1039     case OPT_fguess_branch_probability:

1040       flag_guess_branch_prob = value;

1041       break;

1042

1043     case OPT_fident:

1044       flag_no_ident = !value;

1045       break;

1046

1047     case OPT_fif_conversion:

1048       flag_if_conversion = value;

1049       break;

1050

1051     case OPT_fif_conversion2:

1052       flag_if_conversion2 = value;

1053       break;

1054

1055     case OPT_finhibit_size_directive:

1056       flag_inhibit_size_directive = value;

1057       break;

1058

1059     case OPT_finline:

1060       flag_no_inline = !value;

1061       break;

1062

1063     case OPT_finline_functions:

1064       flag_inline_functions = value;

1065       break;

 

这段代码涉及下列变量。

flag_dump_unnumbered-fdump-unnumbered)当使用-d选项调试编译器时,该选项将在输出文件中压制指令号及行号的产生。这使得用diff来比较转储更方便些。

flag_eliminate_dwarf2_dups-feliminate-dwarf2-dups)如果非0,执行dwarf2的重复消除。

flag_eliminate_unused_debug_types-feliminate-unused-debug-types)如果非0,执行未使用类型消除。

flag_debug_only_used_symbols-feliminate-unused-debug-symbols)如果非0,标识只为使用的符号产生调试信息。

flag_exceptions-fexceptions)如果非0,使能异常处理。这个选项产生额外的,必须的,处理异常抛出及捕获的代码。如果未指明这个选项,诸如AdaJavaC++这些会抛出异常的语言,将自动指定该选项。

flag_finite_math_only-ffinite-math-only)如果非0,表示不期望使用NaNs+-无穷。

flag_no_function_cse-fno-function-cse)如果非0,将导致构成函数调用的指令隐含地包含函数地址。如果为0-ffunction-cse),函数调用通过保存在寄存器中的函数地址完成,这将产生更高效的代码。

flag_float_store-ffloat-store)如果非0,不为浮点值分配寄存器。在某些机器上,这个选项可能导致寄存器扩展精度,超出语言所定义,因此能携带比保存于内存中的浮点值更高精度的值。这个选项仅用于当程序必须限制精度,以严格符号IEEE标准。

flag_force_addr-ffloat-addr)如果非0,地址进行算术操作时,须被拷贝入寄存器。这会改进生成的代码,因为所需的地址通常之前已被加入寄存器,不需要再加载。

flag_function_sections-ffunction-sections)如果非0,在汇编语言输出中,每个函数被置入其自命名的段中。段名由函数名导出。这,仅在具有能使用分段优化空间分配链接器的机器上,带来好处。对于数据项的相同优化参见-fdata-sections。对在其汇编代码中不支持分段的机器,这个选项将导致警告,并被忽略。即便在那些支持的机器上,除非链接器将其用于优化,也不会带来好处。事实上,这可能带来不利的影响,使得目标代码更大,加载更慢。

如果选项-p为概要分析(profiling)设置,这个选项(-fdata-sections)将无效。另外,因为代码重排(rearrangement of the code)的原因,使用-g选项及程序调试可能会有问题。

flag_gcse_lm-fgcse-lm)如果非0,检测循环中的加载、保存操作,其中某些加载操作可以被移至循环前,从而只需做一次。通过此方式执行全局公共子表达式消除优化。这是默认的设置,但除非-Os设置了,它不会有任何作用。该设置亦可通过-no-fgcse-lm改写。

flag_gcse_sm-fgcse-sm)如果非0,检测循环中的加载、保存操作,其中某些保存操作可以被移至循环后,从而只需做一次。通过此方式执行全局公共子表达式消除优化。这是默认的设置,但除非-Os设置了,它不会有任何作用。该设置亦可通过-no-fgcse-sm改写。

flag_gcse_las-fgcse-las)如果非0,全局公共子表达式消除遍消除,跟在对同一内存位置的保存操作后的,加载操作(部分及完全冗余)。

flag_no_ident-fno-ident)如果非0,表示忽略#ident指示。#ident指示带有一个字符串常量参数。在某些系统上,该字符串常量被拷贝入目标文件的特殊的段中。而在其他系统上,该指示被忽略。

flag_inhibit_size_directive-finhibit-size-directive)如果非0,不输出汇编器的.size指示,以及如果函数被分开为位于不同内存块中的2部分时,其他会导致问题的指示。这个选项是用于编译crtstuff.cGCC的一部分)的特例,不期望被用于其它目的。

选项-ffast-math,某些数学计算,如果违反一些ISOIEEE规则,会执行得快一些。例如,设置了这个选项,就会假定,没有负数被传递给sqrt,及所有浮点数都是有效的。设置这个选项导致预处理器定义宏__FAST_MATH__

这个选项由set_fast_math_flags来处理。

 

1590 void

1591 set_fast_math_flags (int set)                                                                              in opts.c

1592 {

1593   flag_trapping_math = !set;

1594   flag_unsafe_math_optimizations = set;

1595   flag_finite_math_only = set;

1596   flag_errno_math = !set;

1597   if (set)

1598   {

1599     flag_signaling_nans = 0;

1600     flag_rounding_math = 0;

1601   }

1602 }

 

在这个函数中

flag_trapping_math-ftrapping-math如果为0表示浮点数学计算不会产生用户可见的陷入trap。这是非停顿IEEE754算术nonstop IEEE 754 arithmetic的一个例子。陷入的条件包括除0,溢出,下溢,无效,不景气,但不包括对信号NaNssignaling NaNs 的操作。

flag_unsafe_math_optimizations-funsafe-math-optimizations)如果非0,表示出于速度考量,允许不安全的浮点算术优化。不保证符号IEEE标准,而且操作允许假定参数及其结果都是“正常的”(例如,传给SQRT是非负数)。

flag_errno_math,如果非0,表示前端期望算术操作维护errno,就像内建的SQRT

flag_signaling_nans-fsignaling-nans)如果非0,表示禁止信号NaNssignaling NaNs)可见的变换。这个选项暗示,在任何IEEE信号NaNs上的操作,都会产生(用户可见)的陷入。

flag_rounding_math-frounding-math)如果非0,表示禁止使用默认浮点取整行为的变换。

 

common_handle_option (continue)

 

1067     case OPT_finline_limit_:

1068     case OPT_finline_limit_eq:

1069       set_param_value ("max-inline-insns-single", value / 2);

1070       set_param_value ("max-inline-insns-auto", value / 2);

1071       set_param_value ("max-inline-insns-rtl", value);

1072       break;

1073

1074     case OPT_finstrument_functions:

1075       flag_instrument_function_entry_exit = value;

1076       break;

1077

1078     case OPT_fkeep_inline_functions:

1079       flag_keep_inline_functions =value;

1080       break;

1081

1082     case OPT_fkeep_static_consts:

1083       flag_keep_static_consts = value;

1084       break;

1085

1086     case OPT_fleading_underscore:

1087       flag_leading_underscore = value;

1088       break;

1089

1090     case OPT_floop_optimize:

1091       flag_loop_optimize = value;

1092       break;

1093

1094     case OPT_fmath_errno:

1095       flag_errno_math = value;

1096       break;

1097

1098     case OPT_fmem_report:

1099       mem_report = value;

1100       break;

1101

1102     case OPT_fmerge_all_constants:

1103       flag_merge_constants = value + value;

1104       break;

1105

1106     case OPT_fmerge_constants:

1107       flag_merge_constants = value;

1108       break;

1109

1110     case OPT_fmessage_length_:

1111       pp_set_line_maximum_length (global_dc->printer, value);

1112       break;

1113

1114     case OPT_fmove_all_movables:

1115       flag_move_all_movables = value;

1116       break;

1117

1118     case OPT_fnew_ra:

1119       flag_new_regalloc = value;

1120       break;

1121

1122     case OPT_fnon_call_exceptions:

1123       flag_non_call_exceptions = value;

1124       break;

1125

1126     case OPT_fold_unroll_all_loops:

1127       flag_old_unroll_all_loops = value;

1128       break;

1129

1130     case OPT_fold_unroll_loops:

1131       flag_old_unroll_loops = value;

1132       break;

1133

1134     case OPT_fomit_frame_pointer:

1135       flag_omit_frame_pointer = value;

1136       break;

1137

1138     case OPT_foptimize_register_move:

1139       flag_regmove = value;

1140       break;

1141

1142     case OPT_foptimize_sibling_calls:

1143       flag_optimize_sibling_calls = value;

1144       break;

1145

1146     case OPT_fpack_struct:

1147       flag_pack_struct = value;

1148       break;

1149

1150     case OPT_fpeel_loops:

1151       flag_peel_loops_set = true;

1152       flag_peel_loops = value;

1153       break;

1154

1155     case OPT_fpcc_struct_return:

1156       flag_pcc_struct_return = value;

1157       break;

1158

1159     case OPT_fpeephole:

1160       flag_no_peephole = !value;

1161       break;

1162

1163     case OPT_fpeephole2:

1164       flag_peephole2 = value;

1165       break;

1166

1167     case OPT_fpic:

1168       flag_pic = value;

1169       break;

1170

1171     case OPT_fpie:

1172       flag_pie = value;

1173       break;

1174

1175     case OPT_fprefetch_loop_arrays:

1176       flag_prefetch_loop_arrays = value;

1177       break;

 

上面,在10671068行,选项-finline-limit–finline-limit=改写了参数"max-inline-insns-single""max-inline-insns-auto""max-inline-insns-rtl"(参考 9:用于i386系统的target_switches)。

flag_instrument_function_entry_exit-finstrument-functions)如果非0,插入代码,在每个函数的进入后和退出前调用一个函数。这些被调用的函数的原型如下:

void __cyg_profile_func_enter(void *this_fn,void *call_site);

void __cyg_profile_func_exit(void *this_fn,void *call_site);

参数this_fn是当前函数的地址,它可由符号表信息来确定。参数call_site确定调用者(caller,在某些平台上call_site信息是得不到的)。如果当前函数被展开为内联,这些函数的调用被插在内联代码的前后。出于识别的目的,必须存在当前函数的非内联版本,即便它所有的调用都被展开为内联代码。

为了防止这些代码的插入,函数可以被声明为具有属性no_instrument_function。这,对于异常处理句柄及不需要调用概要例程(profiling routines)的函数,是必须的。

flag_keep_inline_functions-fkeep-inline-functions)如果非0,编译器将产生函数体,即便对该函数的所有引用都被展开为内联代码,并无实际上的调用。默认的选项是 -fno-keep-inline-functions,这时不被调用的函数不创建函数体。

flag_keep_static_consts-fkeep-static-consts)如果非0,编译单元私有的常量值,即便没有被引用,也为之分配空间。为了防止为未使用的常量分配空间,应使用选项-fno-keep-static-consts

flag_leading_underscore-fleading-underscore)如果非0,强制为每个写入目标文件的符号加上前驱的“-”字符。选项-fno-leading-underscore则压制添加这个字符。这个选项用于需要与传统(legacy)汇编代码链接时。

mem_report-fmem-report)如果非0,当编译器完成任务后,它打印出分配给每个数据类型内存的详细列表及其他内存分配信息。

flag_move_all_movables-fmove-all-movables),如果非0,把所有不变量(invariant)表达式移出循环。是否产生更好的代码取决于源码中循环的结构。除了Fortran,默认的选项是-fno-move-all-movables

flag_new_regalloc-fnew-ra)如果非0,使用图着色寄存器分配器(graph coloring register allocator)。

flag_non_call_exceptions-fnon-call-exceptions)如果非0,产生代码使得陷入指令(例如无效浮点操作或无效内存寻址)抛出异常。这个选项不普遍,因为它要求平台特定的运行时支持。而且这也仅限于硬件陷入信号,不包括普通的信号,例如SIGALRMSIGTERM

flag_old_unroll_all_loops-fold-unroll-all-loops)如果非0,使能unroll.c中的循环展开(loop unrolling)。所有循环皆展开。这通常不是好的选择。

flag_old_unroll_loops-fold-unroll-loops)如果非0,使能unroll.c中的循环展开(loop unrolling)。仅能在编译时刻或运行时刻能确知循环次数的循环被展开。

flag_optimize_sibling_calls-foptimize-sibling-calls)如果非0,优化尾递归调用(recursive tail call)及兄弟调用(sibling call)。这个标识由O2-O3,和-Os自动设置。默认选项是-fno-optimize-sibling-calls。以下是尾递归调用的例子:

int rewhim(int x,int y) {

. . .

return(rewhim(x+1,y));

}

优化可以通过插入代码跳转会函数顶部实现,而不是进行新的函数调用。下面的兄弟调用展示了相似的情形:

int whim(int x,int y) {

. . .

return(wham(x+1,y));

}

在一个兄弟调用中,对函数wham的调用不可避免,但whim的栈框可由调用删除,从而wham直接把返回值返回给whim的调用者。

flag_pack_struct-fpack-struct)如果非0,压紧结构体中的成员,使得成员间没有插入实现对齐的空间。这将导致执行代码以较低的效率访问结构体成员,而且亦可使代码不与系统库兼容。

flag_peel_loops-fpeel-loops)如果非0,使能循环剥离(loop peeling)优化。

flag_no_peephole-fno-peephole)如果非0 禁止窥孔优化(peephole)。

flag_prefetch_loop_arrays-fprefetch-loop-arrays)如果非0,启动循环中数组的预取(prefetch)优化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值