]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/tsan.c
builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
[thirdparty/gcc.git] / gcc / tsan.c
CommitLineData
32b4b7f5
DV
1/* GCC instrumentation plugin for ThreadSanitizer.
2 Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3 Contributed by Dmitry Vyukov <dvyukov@google.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21
22#include "config.h"
23#include "system.h"
24#include "coretypes.h"
25#include "tree.h"
26#include "intl.h"
27#include "tm.h"
28#include "basic-block.h"
29#include "gimple.h"
30#include "function.h"
31#include "tree-flow.h"
32#include "tree-pass.h"
33#include "tree-iterator.h"
34#include "langhooks.h"
35#include "output.h"
36#include "options.h"
37#include "target.h"
38#include "cgraph.h"
39#include "diagnostic.h"
40
41/* Number of instrumented memory accesses in the current function. */
42
43/* Builds the following decl
44 void __tsan_read/writeX (void *addr); */
45
46static tree
47get_memory_access_decl (bool is_write, unsigned size)
48{
49 enum built_in_function fcode;
50
51 if (size <= 1)
52 fcode = is_write ? BUILT_IN_TSAN_WRITE_1
53 : BUILT_IN_TSAN_READ_1;
54 else if (size <= 3)
55 fcode = is_write ? BUILT_IN_TSAN_WRITE_2
56 : BUILT_IN_TSAN_READ_2;
57 else if (size <= 7)
58 fcode = is_write ? BUILT_IN_TSAN_WRITE_4
59 : BUILT_IN_TSAN_READ_4;
60 else if (size <= 15)
61 fcode = is_write ? BUILT_IN_TSAN_WRITE_8
62 : BUILT_IN_TSAN_READ_8;
63 else
64 fcode = is_write ? BUILT_IN_TSAN_WRITE_16
65 : BUILT_IN_TSAN_READ_16;
66
67 return builtin_decl_implicit (fcode);
68}
69
70/* Check as to whether EXPR refers to a store to vptr. */
71
72static tree
73is_vptr_store (gimple stmt, tree expr, bool is_write)
74{
75 if (is_write == true
76 && gimple_assign_single_p (stmt)
77 && TREE_CODE (expr) == COMPONENT_REF)
78 {
79 tree field = TREE_OPERAND (expr, 1);
80 if (TREE_CODE (field) == FIELD_DECL
81 && DECL_VIRTUAL_P (field))
82 return gimple_assign_rhs1 (stmt);
83 }
84 return NULL;
85}
86
87/* Checks as to whether EXPR refers to constant var/field/param.
88 Don't bother to instrument them. */
89
90static bool
91is_load_of_const_p (tree expr, bool is_write)
92{
93 if (is_write)
94 return false;
95 if (TREE_CODE (expr) == COMPONENT_REF)
96 expr = TREE_OPERAND (expr, 1);
97 if (TREE_CODE (expr) == VAR_DECL
98 || TREE_CODE (expr) == PARM_DECL
99 || TREE_CODE (expr) == FIELD_DECL)
100 {
101 if (TREE_READONLY (expr))
102 return true;
103 }
104 return false;
105}
106
107/* Instruments EXPR if needed. If any instrumentation is inserted,
108 * return true. */
109
110static bool
111instrument_expr (gimple_stmt_iterator gsi, tree expr, bool is_write)
112{
113 enum tree_code tcode;
114 tree base, rhs, expr_type, expr_ptr, builtin_decl;
115 basic_block bb;
116 HOST_WIDE_INT size;
117 gimple stmt, g;
118 location_t loc;
119
120 base = get_base_address (expr);
121 if (base == NULL_TREE
122 || TREE_CODE (base) == SSA_NAME
123 || TREE_CODE (base) == STRING_CST)
124 return false;
125
126 tcode = TREE_CODE (expr);
127
128 /* Below are things we do not instrument
129 (no possibility of races or not implemented yet). */
130 if (/* Compiler-emitted artificial variables. */
131 (DECL_P (expr) && DECL_ARTIFICIAL (expr))
132 /* The var does not live in memory -> no possibility of races. */
133 || (tcode == VAR_DECL
134 && !TREE_ADDRESSABLE (expr)
135 && TREE_STATIC (expr) == 0)
136 /* Not implemented. */
137 || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE
138 /* Not implemented. */
139 || tcode == CONSTRUCTOR
140 /* Not implemented. */
141 || tcode == PARM_DECL
142 /* Load of a const variable/parameter/field. */
143 || is_load_of_const_p (expr, is_write))
144 return false;
145
146 size = int_size_in_bytes (TREE_TYPE (expr));
147 if (size == -1)
148 return false;
149
150 /* For now just avoid instrumenting bit field acceses.
151 TODO: handle bit-fields as if touching the whole field. */
152 HOST_WIDE_INT bitsize, bitpos;
153 tree offset;
154 enum machine_mode mode;
155 int volatilep = 0, unsignedp = 0;
156 get_inner_reference (expr, &bitsize, &bitpos, &offset,
157 &mode, &unsignedp, &volatilep, false);
158 if (bitpos % (size * BITS_PER_UNIT)
159 || bitsize != size * BITS_PER_UNIT)
160 return false;
161
162 /* TODO: handle other case: ARRAY_RANGE_REF. */
163 if (tcode != ARRAY_REF
164 && tcode != VAR_DECL
165 && tcode != COMPONENT_REF
166 && tcode != INDIRECT_REF
167 && tcode != MEM_REF)
168 return false;
169
170 stmt = gsi_stmt (gsi);
171 loc = gimple_location (stmt);
172 rhs = is_vptr_store (stmt, expr, is_write);
173 gcc_checking_assert (rhs != NULL || is_gimple_addressable (expr));
174 expr_ptr = build_fold_addr_expr (unshare_expr (expr));
175 if (rhs == NULL)
176 {
177 expr_type = TREE_TYPE (expr);
178 while (TREE_CODE (expr_type) == ARRAY_TYPE)
179 expr_type = TREE_TYPE (expr_type);
180 size = int_size_in_bytes (expr_type);
181 g = gimple_build_call (get_memory_access_decl (is_write, size),
182 1, expr_ptr);
183 }
184 else
185 {
186 builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_VPTR_UPDATE);
187 g = gimple_build_call (builtin_decl, 1, expr_ptr);
188 }
189 gimple_set_location (g, loc);
190 /* Instrumentation for assignment of a function result
191 must be inserted after the call. Instrumentation for
192 reads of function arguments must be inserted before the call.
193 That's because the call can contain synchronization. */
194 if (is_gimple_call (stmt) && is_write)
195 {
196 /* If the call can throw, it must be the last stmt in
197 a basic block, so the instrumented stmts need to be
198 inserted in successor bbs. */
199 if (is_ctrl_altering_stmt (stmt))
200 {
201 edge e;
202
203 bb = gsi_bb (gsi);
204 e = find_fallthru_edge (bb->succs);
205 if (e)
206 gsi_insert_seq_on_edge_immediate (e, g);
207 }
208 else
209 gsi_insert_after (&gsi, g, GSI_NEW_STMT);
210 }
211 else
212 gsi_insert_before (&gsi, g, GSI_SAME_STMT);
213
214 return true;
215}
216
217/* Instruments the gimple pointed to by GSI. Return
218 * true if func entry/exit should be instrumented. */
219
220static bool
221instrument_gimple (gimple_stmt_iterator gsi)
222{
223 gimple stmt;
224 tree rhs, lhs;
225 bool instrumented = false;
226
227 stmt = gsi_stmt (gsi);
228 if (is_gimple_call (stmt)
229 && (gimple_call_fndecl (stmt)
230 != builtin_decl_implicit (BUILT_IN_TSAN_INIT)))
231 return true;
232 else if (is_gimple_assign (stmt))
233 {
234 if (gimple_store_p (stmt))
235 {
236 lhs = gimple_assign_lhs (stmt);
237 instrumented = instrument_expr (gsi, lhs, true);
238 }
239 if (gimple_assign_load_p (stmt))
240 {
241 rhs = gimple_assign_rhs1 (stmt);
242 instrumented = instrument_expr (gsi, rhs, false);
243 }
244 }
245 return instrumented;
246}
247
248/* Instruments all interesting memory accesses in the current function.
249 * Return true if func entry/exit should be instrumented. */
250
251static bool
252instrument_memory_accesses (void)
253{
254 basic_block bb;
255 gimple_stmt_iterator gsi;
256 bool fentry_exit_instrument = false;
257
258 FOR_EACH_BB (bb)
259 for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
260 fentry_exit_instrument |= instrument_gimple (gsi);
261 return fentry_exit_instrument;
262}
263
264/* Instruments function entry. */
265
266static void
267instrument_func_entry (void)
268{
269 basic_block succ_bb;
270 gimple_stmt_iterator gsi;
271 tree ret_addr, builtin_decl;
272 gimple g;
273
274 succ_bb = single_succ (ENTRY_BLOCK_PTR);
275 gsi = gsi_after_labels (succ_bb);
276
277 builtin_decl = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
278 g = gimple_build_call (builtin_decl, 1, integer_zero_node);
279 ret_addr = make_ssa_name (ptr_type_node, NULL);
280 gimple_call_set_lhs (g, ret_addr);
281 gimple_set_location (g, cfun->function_start_locus);
282 gsi_insert_before (&gsi, g, GSI_SAME_STMT);
283
284 builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_FUNC_ENTRY);
285 g = gimple_build_call (builtin_decl, 1, ret_addr);
286 gimple_set_location (g, cfun->function_start_locus);
287 gsi_insert_before (&gsi, g, GSI_SAME_STMT);
288}
289
290/* Instruments function exits. */
291
292static void
293instrument_func_exit (void)
294{
295 location_t loc;
296 basic_block exit_bb;
297 gimple_stmt_iterator gsi;
298 gimple stmt, g;
299 tree builtin_decl;
300 edge e;
301 edge_iterator ei;
302
303 /* Find all function exits. */
304 exit_bb = EXIT_BLOCK_PTR;
305 FOR_EACH_EDGE (e, ei, exit_bb->preds)
306 {
307 gsi = gsi_last_bb (e->src);
308 stmt = gsi_stmt (gsi);
309 gcc_assert (gimple_code (stmt) == GIMPLE_RETURN);
310 loc = gimple_location (stmt);
311 builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_FUNC_EXIT);
312 g = gimple_build_call (builtin_decl, 0);
313 gimple_set_location (g, loc);
314 gsi_insert_before (&gsi, g, GSI_SAME_STMT);
315 }
316}
317
318/* ThreadSanitizer instrumentation pass. */
319
320static unsigned
321tsan_pass (void)
322{
323 if (instrument_memory_accesses ())
324 {
325 instrument_func_entry ();
326 instrument_func_exit ();
327 }
328 return 0;
329}
330
331/* The pass's gate. */
332
333static bool
334tsan_gate (void)
335{
336 return flag_tsan != 0
337 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
338}
339
340/* Inserts __tsan_init () into the list of CTORs. */
341
342void
343tsan_finish_file (void)
344{
345 tree ctor_statements;
346 tree init_decl;
347
348 ctor_statements = NULL_TREE;
349 init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
350 append_to_statement_list (build_call_expr (init_decl, 0),
351 &ctor_statements);
352 cgraph_build_static_cdtor ('I', ctor_statements,
353 MAX_RESERVED_INIT_PRIORITY - 1);
354}
355
356/* The pass descriptor. */
357
358struct gimple_opt_pass pass_tsan =
359{
360 {
361 GIMPLE_PASS,
362 "tsan", /* name */
363 OPTGROUP_NONE, /* optinfo_flags */
364 tsan_gate, /* gate */
365 tsan_pass, /* execute */
366 NULL, /* sub */
367 NULL, /* next */
368 0, /* static_pass_number */
369 TV_NONE, /* tv_id */
370 PROP_ssa | PROP_cfg, /* properties_required */
371 0, /* properties_provided */
372 0, /* properties_destroyed */
373 0, /* todo_flags_start */
374 TODO_verify_all | TODO_update_ssa
375 | TODO_update_address_taken /* todo_flags_finish */
376 }
377};
378
379static bool
380tsan_gate_O0 (void)
381{
382 return flag_tsan != 0 && !optimize
383 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
384}
385
386struct gimple_opt_pass pass_tsan_O0 =
387{
388 {
389 GIMPLE_PASS,
390 "tsan0", /* name */
391 OPTGROUP_NONE, /* optinfo_flags */
392 tsan_gate_O0, /* gate */
393 tsan_pass, /* execute */
394 NULL, /* sub */
395 NULL, /* next */
396 0, /* static_pass_number */
397 TV_NONE, /* tv_id */
398 PROP_ssa | PROP_cfg, /* properties_required */
399 0, /* properties_provided */
400 0, /* properties_destroyed */
401 0, /* todo_flags_start */
402 TODO_verify_all | TODO_update_ssa
403 | TODO_update_address_taken /* todo_flags_finish */
404 }
405};