5.13.5.2.2.1.1.2. 确定输出节
找到或准备好了常量描述符之后,接着在 output_constant_def 中调用下面的函数,尝试在内存中输出这个常量。
2509 static void
2510 maybe_output_constant_def_contents (struct constant_descriptor_tree *desc, in varasm.c
2511 int defer)
2512 {
2513 rtx symbol = XEXP (desc->rtl, 0);
2514 tree exp = desc->value;
2515
2516 if (flag_syntax_only )
2517 return ;
2518
2519 if (TREE_ASM_WRITTEN (exp))
2520 /* Already output; don't do it again. */
2521 return ;
2522
2523 /* The only constants that cannot safely be deferred, assuming the
2524 context allows it, are strings under flag_writable_strings. */
2525 if (defer && (TREE_CODE (exp) != STRING_CST || !flag_writable_strings ))
2526 {
2527 /* Increment n_deferred_constants if it exists. It needs to be at
2528 least as large as the number of constants actually referred to
2529 by the function. If it's too small we'll stop looking too early
2530 and fail to emit constants; if it's too large we'll only look
2531 through the entire function when we could have stopped earlier. */
2532 if (cfun )
2533 n_deferred_constants ++;
2534 return ;
2535 }
2536
2537 output_constant_def_contents (symbol);
2538 }
如果已经发布了汇编代码, TREE_ASM_WRITTEN 将被设置,不需要再次发布。下面 2545 行的 SYMBOL_REF_DECL 是在 build_constant_desc 的 2454 行设置的常量节点的拷贝。为这个节点同样调用 compute_reloc_for_constant 来确定 reloc ,及 output_addressed_constants 输出其中所包含的常量。
2542 static void
2543 output_constant_def_contents (rtx symbol) in varasm.c
2544 {
2545 tree exp = SYMBOL_REF_DECL (symbol);
2546 const char *label = XSTR (symbol, 0);
2547 HOST_WIDE_INT size;
2548
2549 /* Make sure any other constants whose addresses appear in EXP
2550 are assigned label numbers. */
2551 int reloc = compute_reloc_for_constant (exp);
2552
2553 /* Align the location counter as required by EXP's data type. */
2554 int align = TYPE_ALIGN (TREE_TYPE (exp));
2555 #ifdef CONSTANT_ALIGNMENT
2556 align = CONSTANT_ALIGNMENT (exp, align);
2557 #endif
2558
2559 output_addressed_constants (exp);
2560
2561 /* We are no longer deferring this constant. */
2562 TREE_ASM_WRITTEN (exp) = 1;
2563
2564 if (IN_NAMED_SECTION (exp))
2565 named_section (exp, NULL, reloc);
2566 else
2567 (*targetm .asm_out.select_section ) (exp, reloc, align);
2568
2569 if (align > BITS_PER_UNIT)
2570 {
2571 ASM_OUTPUT_ALIGN (asm_out_file , floor_log2 (align / BITS_PER_UNIT));
2572 }
2573
2574 size = int_size_in_bytes (TREE_TYPE (exp));
2575 if (TREE_CODE (exp) == STRING_CST)
2576 size = MAX (TREE_STRING_LENGTH (exp), size);
2577
2578 /* Do any machine/system dependent processing of the constant. */
2579 #ifdef ASM_DECLARE_CONSTANT_NAME
2580 ASM_DECLARE_CONSTANT_NAME (asm_out_file , label, exp, size);
2581 #else
2582 /* Standard thing is just output label for the constant. */
2583 ASM_OUTPUT_LABEL (asm_out_file , label);
2584 #endif /* ASM_DECLARE_CONSTANT_NAME */
2585
2586 /* Output the value of EXP. */
2587 output_constant (exp, size, align);
2588 }
如果已经选定了输出的节( section ),对于 FUNCTION_DECL 及 VAR_DECL 节点, IN_NANED_SECTION 返回 true 。否则调用目标平台钩子 select_section ,它指向下面的函数。
4792 void
4793 default_select_section (tree decl, int reloc, in varasm.c
4794 unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
4795 {
4796 bool readonly = false;
4797
4798 if (DECL_P (decl))
4799 {
4800 if (decl_readonly_section (decl, reloc))
4801 readonly = true;
4802 }
4803 else if (TREE_CODE (decl) == CONSTRUCTOR)
4804 {
4805 if (! ((flag_pic && reloc)
4806 || !TREE_READONLY (decl)
4807 || TREE_SIDE_EFFECTS (decl)
4808 || !TREE_CONSTANT (decl)))
4809 readonly = true;
4810 }
4811 else if (TREE_CODE (decl) == STRING_CST)
4812 readonly = !flag_writable_strings ;
4813 else if (! (flag_pic && reloc))
4814 readonly = true;
4815
4816 if (readonly)
4817 readonly_data_section ();
4818 else
4819 data_section ();
4820 }
上面断言 DECL_P 成立,如果 decl 是一个真正的 DECL 节点。而下面的函数确定 decl 是否是只读的。
4941 bool
4942 decl_readonly_section (tree decl, int reloc) in varasm.c
4943 {
4944 return decl_readonly_section_1 (decl, reloc, flag_pic );
4945 }
变量 flag_pic 保存了选项 –fpic/-fPIC 或 –fpie/-fPIE (如果 –fpic/-fPIC 不出现)的值。【 6 】给出了关于这个选项的描述;
-fpic 产生适用于共享库的位置无关代码( position-independent code – PIC ),如果为目标机器提供支持。这样的代码通过一个全局偏移表( global offset table – GOT )访问所有的常量地址。动态加载器,在程序启动时,解析 GOT 项(动态加载器不是 GCC 的部分;它是操作系统的一部分)。对于链接的可执行映像,如果 GOT 的大小超出了机器指定的最大尺寸,会从链接器得到一个显示‘ -fpic ’不能工作的错误信息;在这个情形下,可使用‘ -fPIC ’重新编译。(这些最大值在 SPARC 上是 8k ,在 m68k 及 RS/6000 上是 32k 。 386 没有这样的限制)。 位置无关代码要求特殊的支持,因此只在某些机器上可用。对于 386 , GCC 向 System V 提供 PIC 支持,但不支持 Sun 386i 。为 IBM RS/6000 产生的代码总是位置无关的。当设置了这个标记时,宏 __pic__ 及 __PIC__ 被定义为 1 。 -fPIC 如果为目标机器提供支持,发布适用于动态链接的位置无关代码,并避免全局偏移表大小的限制。这个选项在 m68k , PowerPC 及 SPARC 上有重要作用。 位置无关代码要求特殊的支持,因此只在某些机器上可用。 当设置了这个标记时,宏 __pic__ 及 __PIC__ 被定义为 2 。 -fpie -fPIE 这些选项类似于‘ -fpic ’及‘ -fPIC ’,不过产生的位置无关代码只能被链入可执行映像。通常,当 GCC 选项‘ -pie ’在链接过程中将被使用时,使用这些选项。 ‘ -fpie ’及‘ -fPIE ’都会定义宏 __pie__ 及 __PIE__ 。这些宏对应‘ -fpie ’具有值 1 ,对应‘ -fPIE ’具有值 2 。 |
4947 bool
4948 decl_readonly_section_1 (tree decl, int reloc, int shlib) in varasm.c
4949 {
4950 switch (categorize_decl_for_section (decl, reloc, shlib))
4951 {
4952 case SECCAT_RODATA:
4953 case SECCAT_RODATA_MERGE_STR:
4954 case SECCAT_RODATA_MERGE_STR_INIT:
4955 case SECCAT_RODATA_MERGE_CONST:
4956 case SECCAT_SRODATA:
4957 return true;
4958 break;
4959 default :
4960 return false;
4961 break ;
4962 }
4963 }
在 section_category 的枚举值的名字中,片段“ _REL ”表示该节包含重定位数据,因此它们被聚集起来,那么动态链接器将访问更少内存页。而“ _RO ”表示该节包含只读数据。这对于预先链接( prelinking )是有用的,因为绝大多数重定位将不是动态地链接,并保持只读。另外“ _LOCAL ”表示该节包含对局部对象的重定位数据。这些重定位将被预先链接完全解析。
4862 static enum section_category
4863 categorize_decl_for_section (tree decl, int reloc, int shlib) in varasm.c
4864 {
4865 enum section_category ret;
4866
4867 if (TREE_CODE (decl) == FUNCTION_DECL)
4868 return SECCAT_TEXT;
4869 else if (TREE_CODE (decl) == STRING_CST)
4870 {
4871 if (flag_writable_strings )
4872 return SECCAT_DATA;
4873 else
4874 return SECCAT_RODATA_MERGE_STR;
4875 }
4876 else if (TREE_CODE (decl) == VAR_DECL)
4877 {
4878 if (DECL_INITIAL (decl) == NULL
4879 || DECL_INITIAL (decl) == error_mark_node)
4880 ret = SECCAT_BSS;
4881 else if (! TREE_READONLY (decl)
4882 || TREE_SIDE_EFFECTS (decl)
4883 || ! TREE_CONSTANT (DECL_INITIAL (decl)))
4884 {
4885 if (shlib && (reloc & 2))
4886 ret = SECCAT_DATA_REL;
4887 else if (shlib && reloc)
4888 ret = SECCAT_DATA_REL_LOCAL;
4889 else
4890 ret = SECCAT_DATA;
4891 }
4892 else if (shlib && (reloc & 2))
4893 ret = SECCAT_DATA_REL_RO;
4894 else if (shlib && reloc)
4895 ret = SECCAT_DATA_REL_RO_LOCAL;
4896 else if (reloc || flag_merge_constants < 2)
4897 /* C and C++ don't allow different variables to share the same
4898 location. -fmerge-all-constants allows even that (at the
4899 expense of not conforming). */
4900 ret = SECCAT_RODATA;
4901 else if (TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
4902 ret = SECCAT_RODATA_MERGE_STR_INIT;
4903 else
4904 ret = SECCAT_RODATA_MERGE_CONST;
4905 }
4906 else if (TREE_CODE (decl) == CONSTRUCTOR)
4907 {
4908 if ((shlib && reloc)
4909 || TREE_SIDE_EFFECTS (decl)
4910 || ! TREE_CONSTANT (decl))
4911 ret = SECCAT_DATA;
4912 else
4913 ret = SECCAT_RODATA;
4914 }
4915 else
4916 ret = SECCAT_RODATA;
4917
4918 /* There are no read-only thread-local sections. */
4919 if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
4920 {
4921 if (ret == SECCAT_BSS)
4922 ret = SECCAT_TBSS;
4923 else
4924 ret = SECCAT_TDATA;
4925 }
4926
4927 /* If the target uses small data sections, select it. */
4928 else if ((*targetm .in_small_data_p) (decl))
4929 {
4930 if (ret == SECCAT_BSS)
4931 ret = SECCAT_SBSS;
4932 else if (targetm .have_srodata_section && ret == SECCAT_RODATA)
4933 ret = SECCAT_SRODATA;
4934 else
4935 ret = SECCAT_SDATA;
4936 }
4937
4938 return ret;
4939 }
除非使用选项‘ -fwritable-string ’,标记 flag_writable_strings 为 false ,这时字符串被视为常量,可与只读数据放在一起。因此选定节 SECCAT_RODATA_MERGE_STR ,在该节中只读数据与字符串合并在一起。
而对于变量( VAR_DECL ),如果没有初始值,依照 C++ 语言的规范,该变量将放在 BSS 节( 4880 行)。如果对象的内容是可改变的( 4881~4883 行条件,注意 4883 行条件检查常量指针初始值的情况),那么如果 reloc 设置了第二位(对于该 VAR_DECL 节点 TREE_PUBLIC 是 true ,参考 compute_reloc_for_constant )并且要求产生 PIC 代码,把该数据放入 SECCAT_DATA_REL 节;若然 reloc 只设置了第一位并且要求产生 PIC 代码,把该数据放入 SECCAT_DATA_REL_LOCAL 节。
在 4919 行, DECL_THREAD_LOCAL 非 0 表示使用线程的局部储存。而在 4928 行,钩子 in_small_data_p 判断该数据是否能放入所谓的小数据节( small data section )。这对于 RISC CPU (例如 PPC )尤为重要,一个具有形式“ Rx + 16bit ”的取址模式可以放入一条 32 位 RISC 指令中,而 32 位绝对地址就要使用混乱得多的周折,把地址放入一个寄存器,然后执行真正的存取。使用小数据节可以尽可能地产生“ Rx + 16bit ”的取址模式。不过, x86 不需要这样的概念。
回到 default_select_section ,如果选定的节是只读属性的,就调用下面的函数。
265 void
266 readonly_data_section (void) in varasm.c
267 {
268 #ifde f READONLY_DATA_SECTION
269 READONLY_DATA_SECTION (); /* Note this can call data_section. */
270 #else
271 #ifdef READONLY_DATA_SECTION_ASM_OP
272 if (in_section != in_readonly_data)
273 {
274 i n_section = in_readonly_data;
275 fputs (READONLY_DATA_SECTION_ASM_OP, asm_out_file );
276 fputc ('/n', asm_out_file );
277 }
278 #else
279 text_section ();
280 #endif
281 #endif
282 }
如果目标平台规定了只读节的命名方式,就要定义宏 READONLY_DATA_SECTION 。而如果目标平台规定了识别只读数据的特定的汇编操作,则需要定义宏 READONLY_DATA_SECTION_ASM_OP 。对于 x86 ,这些宏都没定义,因此调用 279 行的 text_section 。
231 void
232 text_section (void) in varasm.c
233 {
234 if (in_section != in_text)
235 {
236 i n_section = in_text;
237 fprintf (asm_out_file , "%s/n", TEXT_SECTION_ASM_OP);
238 }
239 }
静态变量 in_section 记录当前正在写入节的类型。而 TEXT_SECTION_ASM_OP ,对于绝大多数平台,被定义为 ”/t.text” 。
如果不是只读数据,则是使用下面的函数。
243 void
244 data_section (void) in varasm.c
245 {
246 if (in_section != in_data)
247 {
248 i n_section = in_data;
249 if (flag_shared_data )
250 {
251 #ifdef SHARED_SECTION_ASM_OP
252 fprintf (asm_out_file , "%s/n", SHARED_SECTION_ASM_OP);
253 #else
254 fprintf (asm_out_file , "%s/n", DATA_SECTION_ASM_OP);
255 #endif
256 }
257 else
258 fprintf (asm_out_file , "%s/n", DATA_SECTION_ASM_OP);
259 }
260 }
标记 flag_shared_data 由选项 -fshared-data 设置,这个选项要求此次编译的数据和非 const 变量是共享数据,而不是私有数据。这种差别仅在某些操作系统上面有意义,那里的共享数据在同一个程序的若干进程间共享,而私有数据在每个进程内都有副件。上面可以看到如果定义了 SHARED_SECTION_ASM_OP ,共享数据对该系统才是有意义的。当前只有 IBM 370/MVS , 370/OpenEdition 定义了这个宏。