]>
Commit | Line | Data |
---|---|---|
6de9cd9a DN |
1 | /* Calculate branch probabilities, and basic block execution counts. |
2 | Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, | |
3 | 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. | |
4 | Contributed by James E. Wilson, UC Berkeley/Cygnus Support; | |
5 | based on some ideas from Dain Samples of UC Berkeley. | |
6 | Further mangling by Bob Manson, Cygnus Support. | |
7 | Converted to use trees by Dale Johannesen, Apple Computer. | |
8 | ||
9 | This file is part of GCC. | |
10 | ||
11 | GCC is free software; you can redistribute it and/or modify it under | |
12 | the terms of the GNU General Public License as published by the Free | |
13 | Software Foundation; either version 2, or (at your option) any later | |
14 | version. | |
15 | ||
16 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
17 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
19 | for more details. | |
20 | ||
21 | You should have received a copy of the GNU General Public License | |
22 | along with GCC; see the file COPYING. If not, write to the Free | |
23 | Software Foundation, 59 Temple Place - Suite 330, Boston, MA | |
24 | 02111-1307, USA. */ | |
25 | ||
26 | /* Generate basic block profile instrumentation and auxiliary files. | |
1f1e8527 | 27 | Tree-based version. See profile.c for overview. */ |
6de9cd9a DN |
28 | |
29 | #include "config.h" | |
30 | #include "system.h" | |
31 | #include "coretypes.h" | |
32 | #include "tm.h" | |
33 | #include "rtl.h" | |
34 | #include "flags.h" | |
35 | #include "output.h" | |
36 | #include "regs.h" | |
37 | #include "expr.h" | |
38 | #include "function.h" | |
39 | #include "toplev.h" | |
40 | #include "coverage.h" | |
41 | #include "tree.h" | |
42 | #include "tree-flow.h" | |
43 | #include "tree-dump.h" | |
44 | #include "tree-pass.h" | |
45 | #include "timevar.h" | |
46 | #include "value-prof.h" | |
47 | ||
48 | \f | |
f3df9541 AK |
49 | |
50 | /* Do initialization work for the edge profiler. */ | |
51 | ||
52 | static void | |
53 | tree_init_edge_profiler (void) | |
54 | { | |
55 | } | |
56 | ||
6de9cd9a DN |
57 | /* Output instructions as GIMPLE trees to increment the edge |
58 | execution count, and insert them on E. We rely on | |
59 | bsi_insert_on_edge to preserve the order. */ | |
60 | ||
61 | static void | |
62 | tree_gen_edge_profiler (int edgeno, edge e) | |
63 | { | |
64 | tree tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
65 | tree tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
66 | tree ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno); | |
67 | tree stmt1 = build (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref); | |
68 | tree stmt2 = build (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, | |
69 | build (PLUS_EXPR, GCOV_TYPE_NODE, | |
70 | tmp1, integer_one_node)); | |
71 | tree stmt3 = build (MODIFY_EXPR, GCOV_TYPE_NODE, ref, tmp2); | |
72 | bsi_insert_on_edge (e, stmt1); | |
73 | bsi_insert_on_edge (e, stmt2); | |
74 | bsi_insert_on_edge (e, stmt3); | |
75 | } | |
76 | ||
77 | /* Output instructions as GIMPLE trees to increment the interval histogram | |
78 | counter. VALUE is the expression whose value is profiled. TAG is the | |
79 | tag of the section for counters, BASE is offset of the counter position. */ | |
80 | ||
81 | static void | |
1f1e8527 | 82 | tree_gen_interval_profiler (histogram_value value, unsigned tag, unsigned base) |
6de9cd9a | 83 | { |
1f1e8527 DJ |
84 | tree op, op1, op2, op1copy, op2copy; |
85 | tree tmp1, tmp2, tmp3, val, index; | |
86 | tree label_decl2, label_decl3, label_decl4, label_decl5, label_decl6; | |
87 | edge e12, e23, e34, e45, e56; | |
88 | tree label2, label3, label4, label5, label6; | |
89 | tree stmt1, stmt2, stmt3, stmt4; | |
90 | /* Initializations are to prevent bogus uninitialized warnings. */ | |
91 | tree bb1end = NULL_TREE, bb2end = NULL_TREE, bb3end = NULL_TREE; | |
92 | tree bb4end = NULL_TREE, bb5end = NULL_TREE; | |
93 | tree ref = tree_coverage_counter_ref (tag, base), ref2; | |
94 | basic_block bb2, bb3, bb4, bb5, bb6; | |
95 | tree stmt = value->hvalue.tree.stmt; | |
96 | block_stmt_iterator bsi = bsi_for_stmt (stmt); | |
97 | basic_block bb = bb_for_stmt (stmt); | |
98 | tree optype; | |
99 | ||
100 | op = stmt; | |
101 | if (TREE_CODE (stmt) == RETURN_EXPR | |
102 | && TREE_OPERAND (stmt, 0) | |
103 | && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR) | |
104 | op = TREE_OPERAND (stmt, 0); | |
105 | /* op == MODIFY_EXPR */ | |
106 | op = TREE_OPERAND (op, 1); | |
107 | /* op == TRUNC_DIV or TRUNC_MOD */ | |
108 | op1 = TREE_OPERAND (op, 0); | |
109 | op2 = TREE_OPERAND (op, 1); | |
110 | optype = TREE_TYPE (op); | |
111 | ||
112 | /* Blocks: | |
113 | Original = 1 | |
114 | For 2nd compare = 2 | |
115 | Normal case, neither more nor less = 3 | |
116 | More = 4 | |
117 | Less = 5 | |
118 | End = 6 */ | |
119 | label_decl2 = create_artificial_label (); | |
120 | label_decl3 = create_artificial_label (); | |
121 | label_decl4 = create_artificial_label (); | |
122 | label_decl5 = create_artificial_label (); | |
123 | label_decl6 = create_artificial_label (); | |
124 | ||
125 | /* Do not evaluate op1 or op2 more than once. Probably | |
126 | volatile loads are the only things that could cause | |
127 | a problem, but this is harmless in any case. */ | |
128 | op1copy = create_tmp_var (optype, "PROF"); | |
129 | op2copy = create_tmp_var (optype, "PROF"); | |
130 | stmt1 = build2 (MODIFY_EXPR, optype, op1copy, op1); | |
131 | stmt2 = build2 (MODIFY_EXPR, optype, op2copy, op2); | |
132 | TREE_OPERAND (op, 0) = op1copy; | |
133 | TREE_OPERAND (op, 1) = op2copy; | |
134 | ||
135 | val = create_tmp_var (optype, "PROF"); | |
136 | stmt3 = build2 (MODIFY_EXPR, optype, val, | |
137 | build2 (TRUNC_DIV_EXPR, optype, op1copy, op2copy)); | |
138 | stmt4 = build2 (MODIFY_EXPR, optype, val, | |
139 | build2 (MINUS_EXPR, optype, val, | |
140 | build_int_cst (optype, value->hdata.intvl.int_start))); | |
141 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
142 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
143 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
144 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
145 | ||
146 | index = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
147 | ||
148 | /* Check for too big. */ | |
149 | stmt1 = build3 (COND_EXPR, void_type_node, | |
150 | build2 (GE_EXPR, boolean_type_node, val, | |
151 | build_int_cst (optype, value->hdata.intvl.steps)), | |
152 | build1 (GOTO_EXPR, void_type_node, label_decl4), | |
153 | build1 (GOTO_EXPR, void_type_node, label_decl2)); | |
154 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
155 | bb1end = stmt1; | |
156 | ||
157 | /* Check for too small. */ | |
158 | label2 = build1 (LABEL_EXPR, void_type_node, label_decl2); | |
159 | bsi_insert_before (&bsi, label2, BSI_SAME_STMT); | |
160 | stmt1 = build3 (COND_EXPR, void_type_node, | |
161 | build2 (LT_EXPR, boolean_type_node, val, integer_zero_node), | |
162 | build1 (GOTO_EXPR, void_type_node, label_decl5), | |
163 | build1 (GOTO_EXPR, void_type_node, label_decl3)); | |
164 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
165 | bb2end = stmt1; | |
166 | ||
167 | /* Normal case, within range. */ | |
168 | label3 = build1 (LABEL_EXPR, void_type_node, label_decl3); | |
169 | bsi_insert_before (&bsi, label3, BSI_SAME_STMT); | |
170 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, | |
171 | build1 (NOP_EXPR, GCOV_TYPE_NODE, val)); | |
172 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
173 | bb3end = stmt1; | |
174 | ||
175 | /* Too big */ | |
176 | label4 = build1 (LABEL_EXPR, void_type_node, label_decl4); | |
177 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, | |
178 | build_int_cst (GCOV_TYPE_NODE, value->hdata.intvl.steps)); | |
179 | bsi_insert_before (&bsi, label4, BSI_SAME_STMT); | |
180 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
181 | bb4end = stmt1; | |
182 | ||
183 | /* Too small */ | |
184 | label5 = build1 (LABEL_EXPR, void_type_node, label_decl5); | |
185 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, | |
186 | build_int_cst (GCOV_TYPE_NODE, value->hdata.intvl.steps + 1)); | |
187 | bsi_insert_before (&bsi, label5, BSI_SAME_STMT); | |
188 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
189 | bb5end = stmt1; | |
190 | ||
191 | /* Increment appropriate counter. */ | |
192 | label6 = build1 (LABEL_EXPR, void_type_node, label_decl6); | |
193 | bsi_insert_before (&bsi, label6, BSI_SAME_STMT); | |
194 | ||
195 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
196 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
197 | tmp3 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
198 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, | |
199 | build2 (PLUS_EXPR, GCOV_TYPE_NODE, index, | |
200 | TREE_OPERAND (ref, 1))); | |
201 | TREE_OPERAND (ref, 1) = tmp1; | |
202 | /* Make a copy to avoid sharing complaints. */ | |
203 | ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0), | |
204 | TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2), | |
205 | TREE_OPERAND (ref, 3)); | |
206 | ||
207 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, ref); | |
208 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp3, | |
209 | build2 (PLUS_EXPR, GCOV_TYPE_NODE, tmp2, integer_one_node)); | |
210 | stmt4 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp3); | |
211 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
212 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
213 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
214 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
215 | ||
216 | /* Now fix up the CFG. */ | |
217 | /* 1->2,4; 2->3,5; 3->6; 4->6; 5->6 */ | |
218 | e12 = split_block (bb, bb1end); | |
219 | bb2 = e12->dest; | |
220 | e23 = split_block (bb2, bb2end); | |
221 | bb3 = e23->dest; | |
222 | e34 = split_block (bb3, bb3end); | |
223 | bb4 = e34->dest; | |
224 | e45 = split_block (bb4, bb4end); | |
225 | bb5 = e45->dest; | |
226 | e56 = split_block (bb5, bb5end); | |
227 | bb6 = e56->dest; | |
228 | ||
229 | e12->flags &= ~EDGE_FALLTHRU; | |
230 | e12->flags |= EDGE_FALSE_VALUE; | |
231 | make_edge (bb, bb4, EDGE_TRUE_VALUE); | |
232 | e23->flags &= ~EDGE_FALLTHRU; | |
233 | e23->flags |= EDGE_FALSE_VALUE; | |
234 | make_edge (bb2, bb5, EDGE_TRUE_VALUE); | |
235 | remove_edge (e34); | |
236 | make_edge (bb3, bb6, EDGE_FALLTHRU); | |
237 | remove_edge (e45); | |
238 | make_edge (bb4, bb6, EDGE_FALLTHRU); | |
6de9cd9a DN |
239 | } |
240 | ||
241 | /* Output instructions as GIMPLE trees to increment the power of two histogram | |
242 | counter. VALUE is the expression whose value is profiled. TAG is the tag | |
243 | of the section for counters, BASE is offset of the counter position. */ | |
244 | ||
245 | static void | |
1f1e8527 | 246 | tree_gen_pow2_profiler (histogram_value value, unsigned tag, unsigned base) |
6de9cd9a | 247 | { |
1f1e8527 DJ |
248 | tree op; |
249 | tree tmp1, tmp2, tmp3; | |
250 | tree index, denom; | |
251 | tree label_decl1 = create_artificial_label (); | |
252 | tree label_decl2 = create_artificial_label (); | |
253 | tree label_decl3 = create_artificial_label (); | |
254 | tree label1, label2, label3; | |
255 | tree stmt1, stmt2, stmt3, stmt4; | |
256 | tree bb1end, bb2end, bb3end; | |
257 | tree ref = tree_coverage_counter_ref (tag, base), ref2; | |
258 | basic_block bb2, bb3, bb4; | |
259 | tree stmt = value->hvalue.tree.stmt; | |
260 | block_stmt_iterator bsi = bsi_for_stmt (stmt); | |
261 | basic_block bb = bb_for_stmt (stmt); | |
262 | tree optype, optypesigned, optypeunsigned; | |
263 | ||
264 | op = stmt; | |
265 | if (TREE_CODE (stmt) == RETURN_EXPR | |
266 | && TREE_OPERAND (stmt, 0) | |
267 | && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR) | |
268 | op = TREE_OPERAND (stmt, 0); | |
269 | /* op == MODIFY_EXPR */ | |
270 | op = TREE_OPERAND (op, 1); | |
271 | /* op == TRUNC_DIV or TRUNC_MOD */ | |
272 | op = TREE_OPERAND (op, 1); | |
273 | /* op == denominator */ | |
274 | optype = TREE_TYPE (op); | |
275 | if (TYPE_UNSIGNED (optype)) | |
276 | { | |
277 | /* Right shift must be unsigned. */ | |
278 | optypeunsigned = optype; | |
279 | optypesigned = build_distinct_type_copy (optype); | |
280 | TYPE_UNSIGNED (optypesigned) = false; | |
281 | } | |
282 | else | |
283 | { | |
284 | /* Compare to zero must be signed. */ | |
285 | optypesigned = optype; | |
286 | optypeunsigned = build_distinct_type_copy (optype); | |
287 | TYPE_UNSIGNED (optypeunsigned) = true; | |
288 | } | |
289 | ||
290 | /* Set up variables and check if denominator is negative when considered | |
291 | as signed. */ | |
292 | index = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
293 | denom = create_tmp_var (optype, "PROF"); | |
294 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, integer_zero_node); | |
295 | stmt2 = build2 (MODIFY_EXPR, optype, denom, op); | |
296 | if (optypesigned == optype) | |
297 | { | |
298 | tmp1 = denom; | |
299 | stmt3 = NULL_TREE; | |
300 | } | |
301 | else | |
302 | { | |
303 | tmp1 = create_tmp_var (optypesigned, "PROF"); | |
304 | stmt3 = build2 (MODIFY_EXPR, optypesigned, tmp1, | |
305 | build1 (NOP_EXPR, optypesigned, denom)); | |
306 | } | |
307 | stmt4 = build3 (COND_EXPR, void_type_node, | |
308 | build2 (LE_EXPR, boolean_type_node, tmp1, integer_zero_node), | |
309 | build1 (GOTO_EXPR, void_type_node, label_decl3), | |
310 | build1 (GOTO_EXPR, void_type_node, label_decl1)); | |
311 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
312 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
313 | if (stmt3) | |
314 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
315 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
316 | bb1end = stmt4; | |
317 | ||
318 | /* Nonnegative. Check if denominator is power of 2. */ | |
319 | label1 = build1 (LABEL_EXPR, void_type_node, label_decl1); | |
320 | tmp1 = create_tmp_var (optype, "PROF"); | |
321 | tmp2 = create_tmp_var (optype, "PROF"); | |
322 | stmt1 = build2 (MODIFY_EXPR, optype, tmp1, | |
323 | build2 (PLUS_EXPR, optype, denom, integer_minus_one_node)); | |
324 | stmt2 = build2 (MODIFY_EXPR, optype, tmp2, | |
325 | build2 (BIT_AND_EXPR, optype, tmp1, denom)); | |
326 | stmt3 = build3 (COND_EXPR, void_type_node, | |
327 | build2 (NE_EXPR, boolean_type_node, tmp2, integer_zero_node), | |
328 | build1 (GOTO_EXPR, void_type_node, label_decl3), | |
329 | build1 (GOTO_EXPR, void_type_node, label_decl2)); | |
330 | bsi_insert_before (&bsi, label1, BSI_SAME_STMT); | |
331 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
332 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
333 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
334 | bb2end = stmt3; | |
335 | ||
336 | /* Loop. Increment index, shift denominator, repeat if denominator nonzero. */ | |
337 | label2 = build1 (LABEL_EXPR, void_type_node, label_decl2); | |
338 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, index, | |
339 | build2 (PLUS_EXPR, GCOV_TYPE_NODE, index, integer_one_node)); | |
340 | if (optypeunsigned == optype) | |
341 | { | |
342 | tmp1 = denom; | |
343 | stmt2 = NULL_TREE; | |
344 | } | |
345 | else | |
346 | { | |
347 | tmp1 = create_tmp_var (optypeunsigned, "PROF"); | |
348 | stmt2 = build2 (MODIFY_EXPR, optypeunsigned, tmp1, | |
349 | build1 (NOP_EXPR, optypeunsigned, denom)); | |
350 | } | |
351 | stmt3 = build2 (MODIFY_EXPR, optype, denom, | |
352 | build2 (RSHIFT_EXPR, optype, tmp1, integer_one_node)); | |
353 | stmt4 = build3 (COND_EXPR, void_type_node, | |
354 | build2 (NE_EXPR, boolean_type_node, denom, integer_zero_node), | |
355 | build1 (GOTO_EXPR, void_type_node, label_decl2), | |
356 | build1 (GOTO_EXPR, void_type_node, label_decl3)); | |
357 | bsi_insert_before (&bsi, label2, BSI_SAME_STMT); | |
358 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
359 | if (stmt2) | |
360 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
361 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
362 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
363 | bb3end = stmt4; | |
364 | ||
365 | /* Increment the appropriate counter. */ | |
366 | label3 = build1 (LABEL_EXPR, void_type_node, label_decl3); | |
367 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
368 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
369 | tmp3 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
370 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, | |
371 | build2 (PLUS_EXPR, GCOV_TYPE_NODE, index, TREE_OPERAND (ref, 1))); | |
372 | TREE_OPERAND (ref, 1) = tmp1; | |
373 | /* Make a copy to avoid sharing complaints. */ | |
374 | ref2 = build4 (ARRAY_REF, TREE_TYPE (ref), TREE_OPERAND (ref, 0), | |
375 | TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2), TREE_OPERAND (ref, 3)); | |
376 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, ref); | |
377 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp3, | |
378 | build2 (PLUS_EXPR, GCOV_TYPE_NODE, tmp2, integer_one_node)); | |
379 | stmt4 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp3); | |
380 | bsi_insert_before (&bsi, label3, BSI_SAME_STMT); | |
381 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
382 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
383 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
384 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
385 | ||
386 | /* Now fix up the CFG. */ | |
387 | bb2 = (split_block (bb, bb1end))->dest; | |
388 | bb3 = (split_block (bb2, bb2end))->dest; | |
389 | bb4 = (split_block (bb3, bb3end))->dest; | |
390 | ||
391 | EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU; | |
392 | EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE; | |
393 | make_edge (bb, bb4, EDGE_TRUE_VALUE); | |
394 | ||
395 | EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU; | |
396 | EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE; | |
397 | make_edge (bb2, bb4, EDGE_TRUE_VALUE); | |
398 | ||
399 | EDGE_SUCC (bb3, 0)->flags &= ~EDGE_FALLTHRU; | |
400 | EDGE_SUCC (bb3, 0)->flags |= EDGE_FALSE_VALUE; | |
401 | make_edge (bb3, bb3, EDGE_TRUE_VALUE); | |
6de9cd9a DN |
402 | } |
403 | ||
404 | /* Output instructions as GIMPLE trees for code to find the most common value. | |
405 | VALUE is the expression whose value is profiled. TAG is the tag of the | |
406 | section for counters, BASE is offset of the counter position. */ | |
407 | ||
408 | static void | |
1f1e8527 | 409 | tree_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base) |
6de9cd9a | 410 | { |
1f1e8527 DJ |
411 | tree op; |
412 | tree tmp1, tmp2, tmp3; | |
413 | tree label_decl1 = create_artificial_label (); | |
414 | tree label_decl2 = create_artificial_label (); | |
415 | tree label_decl3 = create_artificial_label (); | |
416 | tree label_decl4 = create_artificial_label (); | |
417 | tree label_decl5 = create_artificial_label (); | |
418 | tree label1, label2, label3, label4, label5; | |
419 | tree stmt1, stmt2, stmt3, stmt4; | |
420 | tree bb1end, bb2end, bb3end, bb4end, bb5end; | |
421 | tree ref1 = tree_coverage_counter_ref (tag, base); | |
422 | tree ref2 = tree_coverage_counter_ref (tag, base + 1); | |
423 | tree ref3 = tree_coverage_counter_ref (tag, base + 2); | |
424 | basic_block bb2, bb3, bb4, bb5, bb6; | |
425 | tree stmt = value->hvalue.tree.stmt; | |
426 | block_stmt_iterator bsi = bsi_for_stmt (stmt); | |
427 | basic_block bb = bb_for_stmt (stmt); | |
428 | tree optype; | |
429 | ||
430 | op = stmt; | |
431 | if (TREE_CODE (stmt) == RETURN_EXPR | |
432 | && TREE_OPERAND (stmt, 0) | |
433 | && TREE_CODE (TREE_OPERAND (stmt, 0)) == MODIFY_EXPR) | |
434 | op = TREE_OPERAND (stmt, 0); | |
435 | /* op == MODIFY_EXPR */ | |
436 | op = TREE_OPERAND (op, 1); | |
437 | /* op == TRUNC_DIV or TRUNC_MOD */ | |
438 | op = TREE_OPERAND (op, 1); | |
439 | /* op == denominator */ | |
440 | optype = TREE_TYPE (op); | |
441 | ||
442 | /* Check if the stored value matches. */ | |
443 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
444 | tmp2 = create_tmp_var (optype, "PROF"); | |
445 | tmp3 = create_tmp_var (optype, "PROF"); | |
446 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref1); | |
447 | stmt2 = build2 (MODIFY_EXPR, optype, tmp2, | |
448 | build1 (NOP_EXPR, optype, tmp1)); | |
449 | stmt3 = build2 (MODIFY_EXPR, optype, tmp3, op); | |
450 | stmt4 = build3 (COND_EXPR, void_type_node, | |
451 | build2 (EQ_EXPR, boolean_type_node, tmp2, tmp3), | |
452 | build1 (GOTO_EXPR, void_type_node, label_decl4), | |
453 | build1 (GOTO_EXPR, void_type_node, label_decl1)); | |
454 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
455 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
456 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
457 | bsi_insert_before (&bsi, stmt4, BSI_SAME_STMT); | |
458 | bb1end = stmt4; | |
459 | ||
460 | /* Does not match; check whether the counter is zero. */ | |
461 | label1 = build1 (LABEL_EXPR, void_type_node, label_decl1); | |
462 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
463 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2); | |
464 | stmt2 = build3 (COND_EXPR, void_type_node, | |
465 | build2 (EQ_EXPR, boolean_type_node, tmp1, integer_zero_node), | |
466 | build1 (GOTO_EXPR, void_type_node, label_decl3), | |
467 | build1 (GOTO_EXPR, void_type_node, label_decl2)); | |
468 | bsi_insert_before (&bsi, label1, BSI_SAME_STMT); | |
469 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
470 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
471 | bb2end = stmt2; | |
472 | ||
473 | /* Counter is not zero yet, decrement. */ | |
474 | label2 = build1 (LABEL_EXPR, void_type_node, label_decl2); | |
475 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
476 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
477 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2); | |
478 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, | |
479 | build (MINUS_EXPR, GCOV_TYPE_NODE, | |
480 | tmp1, integer_one_node)); | |
481 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp2); | |
482 | bsi_insert_before (&bsi, label2, BSI_SAME_STMT); | |
483 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
484 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
485 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
486 | bb3end = stmt3; | |
487 | ||
488 | /* Counter was zero, store new value. */ | |
489 | label3 = build1 (LABEL_EXPR, void_type_node, label_decl3); | |
490 | tmp1 = create_tmp_var (optype, "PROF"); | |
491 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
492 | stmt1 = build2 (MODIFY_EXPR, optype, tmp1, op); | |
493 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, | |
494 | build1 (NOP_EXPR, GCOV_TYPE_NODE, tmp1)); | |
495 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref1, tmp2); | |
496 | bsi_insert_before (&bsi, label3, BSI_SAME_STMT); | |
497 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
498 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
499 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
500 | bb4end = stmt3; | |
501 | /* (fall through) */ | |
502 | ||
503 | /* Increment counter. */ | |
504 | label4 = build1 (LABEL_EXPR, void_type_node, label_decl4); | |
505 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
506 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
507 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref2); | |
508 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, | |
509 | build (PLUS_EXPR, GCOV_TYPE_NODE, | |
510 | tmp1, integer_one_node)); | |
511 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref2, tmp2); | |
512 | bsi_insert_before (&bsi, label4, BSI_SAME_STMT); | |
513 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
514 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
515 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
516 | bb5end = stmt3; | |
517 | ||
518 | /* Increment the counter of all executions; this seems redundant given | |
519 | that we have counts for edges in cfg, but it may happen that some | |
520 | optimization will change the counts for the block (either because | |
521 | it is unable to update them correctly, or because it will duplicate | |
522 | the block or its part). */ | |
523 | label5 = build1 (LABEL_EXPR, void_type_node, label_decl5); | |
524 | tmp1 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
525 | tmp2 = create_tmp_var (GCOV_TYPE_NODE, "PROF"); | |
526 | stmt1 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp1, ref3); | |
527 | stmt2 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, tmp2, | |
528 | build (PLUS_EXPR, GCOV_TYPE_NODE, | |
529 | tmp1, integer_one_node)); | |
530 | stmt3 = build2 (MODIFY_EXPR, GCOV_TYPE_NODE, ref3, tmp2); | |
531 | bsi_insert_before (&bsi, label5, BSI_SAME_STMT); | |
532 | bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT); | |
533 | bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT); | |
534 | bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT); | |
535 | ||
536 | /* Now fix up the CFG. */ | |
537 | bb2 = (split_block (bb, bb1end))->dest; | |
538 | bb3 = (split_block (bb2, bb2end))->dest; | |
539 | bb4 = (split_block (bb3, bb3end))->dest; | |
540 | bb5 = (split_block (bb4, bb4end))->dest; | |
541 | bb6 = (split_block (bb5, bb5end))->dest; | |
542 | ||
543 | EDGE_SUCC (bb, 0)->flags &= ~EDGE_FALLTHRU; | |
544 | EDGE_SUCC (bb, 0)->flags |= EDGE_FALSE_VALUE; | |
545 | make_edge (bb, bb5, EDGE_TRUE_VALUE); | |
546 | ||
547 | EDGE_SUCC (bb2, 0)->flags &= ~EDGE_FALLTHRU; | |
548 | EDGE_SUCC (bb2, 0)->flags |= EDGE_FALSE_VALUE; | |
549 | make_edge (bb2, bb4, EDGE_TRUE_VALUE); | |
550 | ||
551 | remove_edge (EDGE_SUCC (bb3, 0)); | |
552 | make_edge (bb3, bb6, EDGE_FALLTHRU); | |
6de9cd9a DN |
553 | } |
554 | ||
555 | /* Output instructions as GIMPLE trees for code to find the most common value | |
556 | of a difference between two evaluations of an expression. | |
557 | VALUE is the expression whose value is profiled. TAG is the tag of the | |
558 | section for counters, BASE is offset of the counter position. */ | |
559 | ||
560 | static void | |
6d9901e7 | 561 | tree_gen_const_delta_profiler (histogram_value value ATTRIBUTE_UNUSED, |
6de9cd9a DN |
562 | unsigned tag ATTRIBUTE_UNUSED, |
563 | unsigned base ATTRIBUTE_UNUSED) | |
564 | { | |
565 | /* FIXME implement this. */ | |
1e128c5f GB |
566 | #ifdef ENABLE_CHECKING |
567 | internal_error ("unimplemented functionality"); | |
568 | #endif | |
569 | gcc_unreachable (); | |
6de9cd9a DN |
570 | } |
571 | ||
572 | /* Return 1 if tree-based profiling is in effect, else 0. | |
573 | If it is, set up hooks for tree-based profiling. | |
574 | Gate for pass_tree_profile. */ | |
575 | ||
1f1e8527 DJ |
576 | static bool |
577 | do_tree_profiling (void) | |
878f99d2 | 578 | { |
bbd236a1 JH |
579 | if (flag_tree_based_profiling |
580 | && (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)) | |
6de9cd9a DN |
581 | { |
582 | tree_register_profile_hooks (); | |
583 | tree_register_value_prof_hooks (); | |
bbd236a1 | 584 | return true; |
6de9cd9a | 585 | } |
bbd236a1 | 586 | return false; |
6de9cd9a DN |
587 | } |
588 | ||
589 | /* Return the file on which profile dump output goes, if any. */ | |
590 | ||
591 | static FILE *tree_profile_dump_file (void) { | |
592 | return dump_file; | |
593 | } | |
594 | ||
1f1e8527 DJ |
595 | static void |
596 | tree_profiling (void) | |
597 | { | |
598 | branch_prob (); | |
599 | if (flag_branch_probabilities | |
600 | && flag_profile_values | |
601 | && flag_value_profile_transformations) | |
602 | value_profile_transformations (); | |
603 | /* The above could hose dominator info. Currently there is | |
604 | none coming in, this is a safety valve. It should be | |
605 | easy to adjust it, if and when there is some. */ | |
606 | free_dominance_info (CDI_DOMINATORS); | |
607 | free_dominance_info (CDI_POST_DOMINATORS); | |
608 | } | |
609 | ||
6de9cd9a DN |
610 | struct tree_opt_pass pass_tree_profile = |
611 | { | |
612 | "tree_profile", /* name */ | |
613 | do_tree_profiling, /* gate */ | |
1f1e8527 | 614 | tree_profiling, /* execute */ |
6de9cd9a DN |
615 | NULL, /* sub */ |
616 | NULL, /* next */ | |
617 | 0, /* static_pass_number */ | |
618 | TV_BRANCH_PROB, /* tv_id */ | |
619 | PROP_gimple_leh | PROP_cfg, /* properties_required */ | |
620 | PROP_gimple_leh | PROP_cfg, /* properties_provided */ | |
621 | 0, /* properties_destroyed */ | |
622 | 0, /* todo_flags_start */ | |
9f8628ba PB |
623 | TODO_verify_stmts, /* todo_flags_finish */ |
624 | 0 /* letter */ | |
6de9cd9a DN |
625 | }; |
626 | ||
627 | struct profile_hooks tree_profile_hooks = | |
628 | { | |
f3df9541 | 629 | tree_init_edge_profiler, /* init_edge_profiler */ |
6de9cd9a DN |
630 | tree_gen_edge_profiler, /* gen_edge_profiler */ |
631 | tree_gen_interval_profiler, /* gen_interval_profiler */ | |
632 | tree_gen_pow2_profiler, /* gen_pow2_profiler */ | |
633 | tree_gen_one_value_profiler, /* gen_one_value_profiler */ | |
634 | tree_gen_const_delta_profiler,/* gen_const_delta_profiler */ | |
635 | tree_profile_dump_file /* profile_dump_file */ | |
636 | }; |