1 /* Function re-ordering plugin for gold.
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 Contributed by Sriraman Tallam (tmsriram@google.com)
4 and Easwaran Raman (eraman@google.com).
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
20 /* This plugin should be invoked only when callgraph edge profile
21 information is available in the object files generated using the
22 compiler flag -fcallgraph-profiles-sections. The callgraph edge
23 profiles are stored in special sections marked .gnu.callgraph.*
25 This plugin reads the callgraph sections and constructs an annotated
26 callgraph. It then repeatedly groups sections that are connected by
27 hot edges and passes the new function layout to the linker. The
28 layout is based on the procedure reordering algorithm described
31 "Profile guided code positioning", K. Pettis, R. Hansen
32 Proceedings of PLDI 1990.
34 This plugin dumps the final layout order of the functions in a file
35 called "final_layout.txt". To change the output file, pass the new
36 file name with --plugin-opt. To dump to stderr instead, just pass
37 stderr to --plugin-opt. */
51 #include "plugin-api.h"
52 #include "callgraph.h"
54 /* #include <elf.h> Not available on Darwin.
55 Rather than dealing with cross-compilation includes, hard code the
56 values we need, as these will not change. */
61 #define SHT_PROGBITS 1
64 enum ld_plugin_status
claim_file_hook (const struct ld_plugin_input_file
*file
,
66 enum ld_plugin_status
all_symbols_read_hook ();
68 static ld_plugin_register_claim_file register_claim_file_hook
= NULL
;
69 static ld_plugin_register_all_symbols_read
70 register_all_symbols_read_hook
= NULL
;
71 static ld_plugin_get_input_section_count get_input_section_count
= NULL
;
72 static ld_plugin_get_input_section_type get_input_section_type
= NULL
;
73 static ld_plugin_get_input_section_name get_input_section_name
= NULL
;
74 static ld_plugin_get_input_section_contents get_input_section_contents
= NULL
;
75 static ld_plugin_update_section_order update_section_order
= NULL
;
76 static ld_plugin_allow_section_ordering allow_section_ordering
= NULL
;
78 /* The file where the final function order will be stored.
79 It can be set by using the plugin option as --plugin-opt
80 "file=<name>". To dump to stderr, say --plugin-opt "file=stderr". */
82 static char *out_file
= NULL
;
84 static int is_api_exist
= 0;
86 /* The plugin does nothing when no-op is 1. */
89 /* Copies new output file name out_file */
90 void get_filename (const char *name
)
92 XNEWVEC_ALLOC (out_file
, char, (strlen (name
) + 1));
93 strcpy (out_file
, name
);
96 /* Process options to plugin. Options with prefix "group=" are special.
97 They specify the type of grouping. The option "group=none" makes the
98 plugin do nothing. Options with prefix "file=" set the output file
99 where the final function order must be stored. */
101 process_option (const char *name
)
103 const char *option_group
= "group=";
104 const char *option_file
= "file=";
106 /* Check if option is "group=" */
107 if (strncmp (name
, option_group
, strlen (option_group
)) == 0)
109 if (strcmp (name
+ strlen (option_group
), "none") == 0)
116 /* Check if option is "file=" */
117 if (strncmp (name
, option_file
, strlen (option_file
)) == 0)
119 get_filename (name
+ strlen (option_file
));
123 /* Unknown option, set no_op to 1. */
125 fprintf (stderr
, "Unknown option to function reordering plugin :%s\n",
129 /* Plugin entry point. */
130 enum ld_plugin_status
131 onload (struct ld_plugin_tv
*tv
)
133 struct ld_plugin_tv
*entry
;
134 for (entry
= tv
; entry
->tv_tag
!= LDPT_NULL
; ++entry
)
136 switch (entry
->tv_tag
)
138 case LDPT_API_VERSION
:
140 case LDPT_GOLD_VERSION
:
143 process_option (entry
->tv_u
.tv_string
);
144 /* If no_op is set, do not do anything else. */
145 if (no_op
) return LDPS_OK
;
147 case LDPT_REGISTER_CLAIM_FILE_HOOK
:
148 register_claim_file_hook
= *entry
->tv_u
.tv_register_claim_file
;
150 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK
:
151 register_all_symbols_read_hook
152 = *entry
->tv_u
.tv_register_all_symbols_read
;
154 case LDPT_GET_INPUT_SECTION_COUNT
:
155 get_input_section_count
= *entry
->tv_u
.tv_get_input_section_count
;
157 case LDPT_GET_INPUT_SECTION_TYPE
:
158 get_input_section_type
= *entry
->tv_u
.tv_get_input_section_type
;
160 case LDPT_GET_INPUT_SECTION_NAME
:
161 get_input_section_name
= *entry
->tv_u
.tv_get_input_section_name
;
163 case LDPT_GET_INPUT_SECTION_CONTENTS
:
164 get_input_section_contents
= *entry
->tv_u
.tv_get_input_section_contents
;
166 case LDPT_UPDATE_SECTION_ORDER
:
167 update_section_order
= *entry
->tv_u
.tv_update_section_order
;
169 case LDPT_ALLOW_SECTION_ORDERING
:
170 allow_section_ordering
= *entry
->tv_u
.tv_allow_section_ordering
;
179 if (register_all_symbols_read_hook
!= NULL
180 && register_claim_file_hook
!= NULL
181 && get_input_section_count
!= NULL
182 && get_input_section_type
!= NULL
183 && get_input_section_name
!= NULL
184 && get_input_section_contents
!= NULL
185 && update_section_order
!= NULL
186 && allow_section_ordering
!= NULL
)
191 /* Register handlers. */
192 assert ((*register_all_symbols_read_hook
) (all_symbols_read_hook
)
194 assert ((*register_claim_file_hook
) (claim_file_hook
)
199 static int is_ordering_specified
= 0;
201 /* This function is called by the linker for every new object it encounters. */
203 enum ld_plugin_status
204 claim_file_hook (const struct ld_plugin_input_file
*file
, int *claimed
)
206 unsigned int count
= 0;
207 struct ld_plugin_section section
;
212 /* Plugin APIs are supported if this is called. */
213 assert (is_api_exist
);
215 if (is_ordering_specified
== 0)
217 /* Inform the linker to prepare for section reordering. */
218 (*allow_section_ordering
) ();
219 is_ordering_specified
= 1;
222 (*get_input_section_count
) (file
->handle
, &count
);
224 for (shndx
= 0; shndx
< count
; ++shndx
)
226 unsigned int type
= SHT_NULL
;
229 section
.handle
= file
->handle
;
230 section
.shndx
= shndx
;
231 (*get_input_section_type
) (section
, &type
);
233 (*get_input_section_name
) (section
, &name
);
234 push_allocated_ptr (name
);
235 if (type
== SHT_PROGBITS
&& is_prefix_of (".text.", name
))
237 map_section_name_to_index (name
, file
->handle
, shndx
);
239 else if (is_prefix_of (".gnu.callgraph.text", name
))
241 /* Process callgraph sections. */
242 unsigned char *section_contents_ptr
= NULL
;
244 (*get_input_section_contents
) (section
,
245 (const unsigned char **)§ion_contents_ptr
,
247 unsigned char *section_contents
;
248 XNEWVEC_ALLOC (section_contents
, unsigned char, length
);
249 memcpy (section_contents
, section_contents_ptr
, length
);
250 parse_callgraph_section_contents (file
->handle
,
252 (unsigned int)length
);
259 /* This function is called by the linker after all the symbols have been read.
260 At this stage, it is fine to tell the linker the desired function order. */
262 enum ld_plugin_status
263 all_symbols_read_hook (void)
265 unsigned int num_entries
;
267 struct ld_plugin_section
*section_list
;
272 /* Plugin APIs are supported if this is called. */
273 assert (is_api_exist
);
275 if (is_callgraph_empty ())
278 /* Open the file to write the final layout */
279 if (out_file
!= NULL
)
281 if (strcmp (out_file
, "stderr") == 0)
284 fp
= fopen (out_file
, "w");
286 fprintf (fp
, "# Remove lines starting with \'#\' to"
287 " pass to --section-ordering-file\n");
288 fprintf (fp
, "# Lines starting with \'#\' are the edge profiles\n");
291 find_pettis_hansen_function_layout (fp
);
292 num_entries
= get_layout (fp
, &handles
, &shndx
);
293 XNEWVEC_ALLOC (section_list
, struct ld_plugin_section
, num_entries
);
295 for (i
= 0; i
< num_entries
; i
++)
297 section_list
[i
].handle
= handles
[i
];
298 section_list
[i
].shndx
= shndx
[i
];
302 && strcmp (out_file
, "stderr") != 0)
304 /* Pass the new order of functions to the linker. */
305 update_section_order (section_list
, num_entries
);