From patchwork Wed Oct 31 18:34:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Mi X-Patchwork-Id: 195985 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 6D6A22C014C for ; Thu, 1 Nov 2012 05:34:38 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1352313279; h=Comment: DomainKey-Signature:Received:Received:Received:Received: MIME-Version:Received:Received:Date:Message-ID:Subject:From:To: Cc:Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=y71LHB7 ITVbElLfv98FSF8oYty0=; b=csGEPIaKYPzbCa2snN4cv07FyYi+MBno37AB4kY eBWf7ppVJzuXukhPoV/aDqlXiwa7EBQM9TDmUaLbJZ6Rkde7wOWiO6gvWoIJDdud P3KE56Am1Jbu7vlRT/RvxFa1AvqnpxNWiMhdtVjXZ6mk3wGp6ATQliBxzY7DW4Nt D5wk= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:MIME-Version:Received:Received:Date:Message-ID:Subject:From:To:Cc:Content-Type:X-System-Of-Record:X-Gm-Message-State:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=XQI6QqKFW7AeWUlAG5Thmit+r/hcitSREj2a0+FXyZ4nCM7VLcTPN1sH9iAcM+ 6jS963YfW4OW9Vh9QKtbgfn1MvrxMX3IpRuTseP3GMwIjnfFHcoE26XumyI+iZ8M aeGtOt+fKt0kb2GTG9bQzJB7cTG5GjyuZYIA9MVYNowuo=; Received: (qmail 1444 invoked by alias); 31 Oct 2012 18:34:24 -0000 Received: (qmail 1291 invoked by uid 22791); 31 Oct 2012 18:34:22 -0000 X-SWARE-Spam-Status: No, hits=-5.6 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, TW_CF, TW_TM, TW_VP X-Spam-Check-By: sourceware.org Received: from mail-qc0-f175.google.com (HELO mail-qc0-f175.google.com) (209.85.216.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 31 Oct 2012 18:34:11 +0000 Received: by mail-qc0-f175.google.com with SMTP id j3so1282696qcs.20 for ; Wed, 31 Oct 2012 11:34:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:date:message-id:subject:from:to:cc:content-type :x-system-of-record:x-gm-message-state; bh=Jmh+dch8ZzYc2qa4K9fPNz7MFtwNICS0NwqBXV15WUI=; b=SWtHt63KaO8p/k2TNWlcNkzP29wKDzRBgDdZCCRdVqF3DxmUlOJN7a69kNotMddMae LHIsw93EJqZcJBNW8LD58Y8shsomhKO+Adf6XH8JGOxYvk/5Zqe9v5lzIFQNOJ86b/la hcrU0wJIW07Vw3hEYW/TbIUx/yI9Bd9EhkAqrIM7Uewb9ikV8+seEIIJEbSkYP9e9tG+ dPSo9kL4IzaqEG8pDQpZ3tvaV+8GjfES2uknavSllfQ8T92NPrZU12RrTzslGuxI+yre nqswwSmRsVEFNKkBJH6xNklBFOtPLXllmXfwDdk9nKQ56ncl74OVrO/SIn/5PHXlfQp6 9eAA== MIME-Version: 1.0 Received: by 10.224.10.136 with SMTP id p8mr3689455qap.50.1351708450934; Wed, 31 Oct 2012 11:34:10 -0700 (PDT) Received: by 10.49.87.164 with HTTP; Wed, 31 Oct 2012 11:34:10 -0700 (PDT) Date: Wed, 31 Oct 2012 11:34:10 -0700 Message-ID: Subject: [tsan] ThreadSanitizer instrumentation part From: Wei Mi To: gcc-patches@gcc.gnu.org Cc: Dmitry Vyukov , David Li , Diego Novillo , Jakub Jelinek , Dodji Seketeli X-System-Of-Record: true X-Gm-Message-State: ALoCoQl9fsShBi6ySp+h36SLcyU1B01idnDG30jAR3HktjQY23hLFzzUHI530lkQ3Cia5I4+d4FuEGl1Nqv+kxbOXmmqyVdTpZhDurwgvURHZ6SKMztx3eI2KiWT3GRE3lt2unuKmznotfRDq3MbR5m8AnhgJDotwz64v4bIybAXAjJFv3gEhVe1urd63eKw6TmRHQJRMKJB Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, The patch is about ThreadSanitizer. ThreadSanitizer is a data race detector for C/C++ programs. It contains two parts: instrumentation and runtime library. This patch is the first part, and runtime will be included in the second part. Dmitry(dvyukov@google.com) is the author of this part, and I try to migrate it to trunk. Ok for trunk? gcc/ChangeLog: 2012-10-31 Wei Mi * Makefile.in (tsan.o): New * passes.c (init_optimization_passes): Add tsan passes * tree-pass.h (register_pass_info): Ditto * cfghooks.h (GCC_CFGHOOKS_H): Avoid including duplicate headers * doc/invoke.texi: Document tsan related options * toplev.c (compile_file): Add tsan pass in driver * gcc.c (LINK_COMMAND_SPEC): Add -lasan in link command if there -ftsan is on. * tsan.c: New file about tsan * tsan.h: Ditto Please check the following links for background: http://code.google.com/p/data-race-test http://gcc.gnu.org/wiki/cauldron2012?action=AttachFile&do=get&target=kcc.pdf (the second half is about ThreadSanitizer). A small testcase race_on_heap.cc is attached to show its functionality. Run the small testcase with -ftsan produce the following warning: WARNING: ThreadSanitizer: data race (pid=5978) Write of size 4 at 0x7d0600039040 by thread 3: #0 Thread2(void*) ??:0 (exe+0x0000000052c0) Previous write of size 4 at 0x7d0600039040 by thread 2: #0 Thread1(void*) ??:0 (exe+0x00000000527d) Location is heap block of size 99 at 0x7d0600039030 allocated by thread 1: #0 malloc /usr/local/google/home/wmi/Work/llvm-main/trunk/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:293 (exe+0x00000000e9ce) #1 alloc() ??:0 (exe+0x0000000052fc) #2 AllocThread(void*) ??:0 (exe+0x00000000532c) Thread 3 (tid=5981, running) created at: #0 pthread_create /usr/local/google/home/wmi/Work/llvm-main/trunk/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:645 (exe+0x00000000bf1d) #1 main ??:0 (exe+0x000000005433) Thread 2 (tid=5980, finished) created at: #0 pthread_create /usr/local/google/home/wmi/Work/llvm-main/trunk/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:645 (exe+0x00000000bf1d) #1 main ??:0 (exe+0x000000005400) Thread 1 (tid=5979, finished) created at: #0 pthread_create /usr/local/google/home/wmi/Work/llvm-main/trunk/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:645 (exe+0x00000000bf1d) #1 main ??:0 (exe+0x000000005384) Thanks, Wei. Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 193016) +++ gcc/Makefile.in (working copy) @@ -1351,6 +1351,7 @@ OBJS = \ trans-mem.o \ tree-affine.o \ tree-call-cdce.o \ + tsan.o \ tree-cfg.o \ tree-cfgcleanup.o \ tree-chrec.o \ @@ -2618,6 +2619,12 @@ tree-nomudflap.o : $(CONFIG_H) $(SYSTEM_ $(C_TREE_H) $(C_COMMON_H) $(GIMPLE_H) $(DIAGNOSTIC_H) $(HASHTAB_H) \ output.h langhooks.h tree-mudflap.h $(TM_H) coretypes.h \ $(GGC_H) gt-tree-mudflap.h $(TREE_PASS_H) $(DIAGNOSTIC_CORE_H) +tsan.o : $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TREE_INLINE_H) \ + $(GIMPLE_H) $(DIAGNOSTIC_H) langhooks.h \ + $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(CGRAPH_H) $(GGC_H) \ + $(BASIC_BLOCK_H) $(FLAGS_H) $(FUNCTION_H) \ + $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_CORE_H) $(GIMPLE_H) tree-iterator.h \ + intl.h cfghooks.h output.h options.h c-family/c-common.h tsan.h tree-pretty-print.o : tree-pretty-print.c $(CONFIG_H) $(SYSTEM_H) \ $(TREE_H) $(DIAGNOSTIC_H) $(HASHTAB_H) $(TREE_FLOW_H) \ $(TM_H) coretypes.h dumpfile.h tree-iterator.h $(SCEV_H) langhooks.h \ @@ -2671,7 +2678,8 @@ toplev.o : toplev.c $(CONFIG_H) $(SYSTEM $(CGRAPH_H) $(COVERAGE_H) alloc-pool.h $(GGC_H) \ $(OPTS_H) params.def tree-mudflap.h $(TREE_PASS_H) $(GIMPLE_H) \ tree-ssa-alias.h $(PLUGIN_H) realmpfr.h tree-diagnostic.h \ - $(TREE_PRETTY_PRINT_H) opts-diagnostic.h $(COMMON_TARGET_H) + $(TREE_PRETTY_PRINT_H) opts-diagnostic.h $(COMMON_TARGET_H) \ + tsan.h hwint.o : hwint.c $(CONFIG_H) $(SYSTEM_H) $(DIAGNOSTIC_CORE_H) Index: gcc/passes.c =================================================================== --- gcc/passes.c (revision 193016) +++ gcc/passes.c (working copy) @@ -1439,6 +1439,7 @@ init_optimization_passes (void) NEXT_PASS (pass_split_crit_edges); NEXT_PASS (pass_pre); NEXT_PASS (pass_sink_code); + NEXT_PASS (pass_tsan); NEXT_PASS (pass_tree_loop); { struct opt_pass **p = &pass_tree_loop.pass.sub; @@ -1544,6 +1545,7 @@ init_optimization_passes (void) NEXT_PASS (pass_tm_edges); } NEXT_PASS (pass_lower_complex_O0); + NEXT_PASS (pass_tsan_O0); NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_lower_resx); NEXT_PASS (pass_nrv); Index: gcc/tree-pass.h =================================================================== --- gcc/tree-pass.h (revision 193016) +++ gcc/tree-pass.h (working copy) @@ -256,6 +256,8 @@ struct register_pass_info extern struct gimple_opt_pass pass_mudflap_1; extern struct gimple_opt_pass pass_mudflap_2; +extern struct gimple_opt_pass pass_tsan; +extern struct gimple_opt_pass pass_tsan_O0; extern struct gimple_opt_pass pass_lower_cf; extern struct gimple_opt_pass pass_refactor_eh; extern struct gimple_opt_pass pass_lower_eh; Index: gcc/cfghooks.h =================================================================== --- gcc/cfghooks.h (revision 193016) +++ gcc/cfghooks.h (working copy) @@ -19,6 +19,9 @@ You should have received a copy of the G along with GCC; see the file COPYING3. If not see . */ +#ifndef GCC_CFGHOOKS_H +#define GCC_CFGHOOKS_H + /* Only basic-block.h includes this. */ struct cfg_hooks @@ -219,3 +222,4 @@ extern void gimple_register_cfg_hooks (v extern struct cfg_hooks get_cfg_hooks (void); extern void set_cfg_hooks (struct cfg_hooks); +#endif /* GCC_CFGHOOKS_H */ Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (revision 193016) +++ gcc/doc/invoke.texi (working copy) @@ -308,6 +308,7 @@ Objective-C and Objective-C++ Dialects}. -fdump-tree-ssa@r{[}-@var{n}@r{]} -fdump-tree-pre@r{[}-@var{n}@r{]} @gol -fdump-tree-ccp@r{[}-@var{n}@r{]} -fdump-tree-dce@r{[}-@var{n}@r{]} @gol -fdump-tree-gimple@r{[}-raw@r{]} -fdump-tree-mudflap@r{[}-@var{n}@r{]} @gol +-fdump-tree-tsan@r{[}-@var{n}@r{]} @gol -fdump-tree-dom@r{[}-@var{n}@r{]} @gol -fdump-tree-dse@r{[}-@var{n}@r{]} @gol -fdump-tree-phiprop@r{[}-@var{n}@r{]} @gol @@ -380,8 +381,8 @@ Objective-C and Objective-C++ Dialects}. -floop-parallelize-all -flto -flto-compression-level @gol -flto-partition=@var{alg} -flto-report -fmerge-all-constants @gol -fmerge-constants -fmodulo-sched -fmodulo-sched-allow-regmoves @gol --fmove-loop-invariants fmudflap -fmudflapir -fmudflapth -fno-branch-count-reg @gol --fno-default-inline @gol +-fmove-loop-invariants -fmudflap -fmudflapir -fmudflapth -fno-branch-count-reg @gol +-ftsan -ftsan-ignore -fno-default-inline @gol -fno-defer-pop -fno-function-cse -fno-guess-branch-probability @gol -fno-inline -fno-math-errno -fno-peephole -fno-peephole2 @gol -fno-sched-interblock -fno-sched-spec -fno-signed-zeros @gol @@ -5956,6 +5957,11 @@ appending @file{.dce} to the source file Dump each function after adding mudflap instrumentation. The file name is made by appending @file{.mudflap} to the source file name. +@item tsan +@opindex fdump-tree-tsan +Dump each function after adding ThreadSanitizer instrumentation. The file name is +made by appending @file{.tsan} to the source file name. + @item sra @opindex fdump-tree-sra Dump each function after performing scalar replacement of aggregates. The @@ -6798,6 +6804,12 @@ instrumentation (and therefore faster ex some protection against outright memory corrupting writes, but allows erroneously read data to propagate within a program. +@item -ftsan -ftsan-ignore +@opindex ftsan +@opindex ftsan-ignore +Add ThreadSanitizer instrumentation. Use @option{-ftsan-ignore} to specify +an ignore file. Refer to http://go/tsan for details. + @item -fthread-jumps @opindex fthread-jumps Perform optimizations that check to see if a jump branches to a Index: gcc/toplev.c =================================================================== --- gcc/toplev.c (revision 193016) +++ gcc/toplev.c (working copy) @@ -73,6 +73,7 @@ along with GCC; see the file COPYING3. #include "alloc-pool.h" #include "tree-mudflap.h" #include "gimple.h" +#include "tsan.h" #include "tree-ssa-alias.h" #include "plugin.h" @@ -570,6 +571,10 @@ compile_file (void) if (flag_mudflap) mudflap_finish_file (); + /* File-scope initialization for ThreadSanitizer. */ + if (flag_tsan) + tsan_finish_file (); + output_shared_constant_pool (); output_object_blocks (); finish_tm_clone_pairs (); Index: gcc/gcc.c =================================================================== --- gcc/gcc.c (revision 193016) +++ gcc/gcc.c (working copy) @@ -679,6 +679,7 @@ proper position among the other output f %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\ %(mflib) " STACK_SPLIT_SPEC "\ %{fprofile-arcs|fprofile-generate*|coverage:-lgcov}\ + %{ftsan:-ltsan}\ %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\ %{!nostdlib:%{!nostartfiles:%E}} %{T*} }}}}}}" #endif Index: gcc/tsan.c =================================================================== --- gcc/tsan.c (revision 0) +++ gcc/tsan.c (revision 0) @@ -0,0 +1,534 @@ +/* GCC instrumentation plugin for ThreadSanitizer. + * Copyright (c) 2012, Google Inc. All rights reserved. + * Author: Dmitry Vyukov (dvyukov) + * + * IT is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3, or (at your option) any later + * version. See http://www.gnu.org/licenses/ + */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "intl.h" +#include "tm.h" +#include "basic-block.h" +#include "gimple.h" +#include "function.h" +#include "tree-flow.h" +#include "tree-pass.h" +#include "tree-iterator.h" +#include "cfghooks.h" +#include "langhooks.h" +#include "output.h" +#include "options.h" +#include "target.h" +#include "cgraph.h" +#include "diagnostic.h" + +/* Number of instrumented memory accesses in the current function. */ + +static int func_mops; + +/* Number of function calls in the current function. */ + +static int func_calls; + +/* Returns a definition of a runtime functione with type TYP and name NAME. */ + +static tree +build_func_decl (tree typ, const char *name) +{ + tree decl; + + decl = build_fn_decl (name, typ); + TREE_NOTHROW (decl) = 1; + DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("leaf"), + NULL, DECL_ATTRIBUTES (decl)); + DECL_ASSEMBLER_NAME (decl); + return decl; +} + +/* Builds the following decl + void __tsan_read/writeX (void *addr); */ + +static tree +get_memory_access_decl (int is_write, unsigned size) +{ + tree typ, *decl; + char fname [64]; + static tree cache [2][17]; + + is_write = !!is_write; + if (size <= 1) + size = 1; + else if (size <= 3) + size = 2; + else if (size <= 7) + size = 4; + else if (size <= 15) + size = 8; + else + size = 16; + decl = &cache[is_write][size]; + if (*decl == NULL) + { + snprintf(fname, sizeof fname, "__tsan_%s%d", + is_write ? "write" : "read", size); + typ = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + *decl = build_func_decl (typ, fname); + } + return *decl; +} + +/* Builds the following decl + void __tsan_vptr_update (void *vptr, void *val); */ + +static tree +get_vptr_update_decl (void) +{ + tree typ; + static tree decl; + + if (decl != NULL) + return decl; + typ = build_function_type_list (void_type_node, + ptr_type_node, ptr_type_node, NULL_TREE); + decl = build_func_decl (typ, "__tsan_vptr_update"); + return decl; +} + + +/* Builds the following decl + void __tsan_init (void); */ + +static tree +get_init_decl (void) +{ + tree typ; + static tree decl; + + if (decl != NULL) + return decl; + typ = build_function_type_list (void_type_node, NULL_TREE); + decl = build_func_decl (typ, "__tsan_init"); + return decl; +} + +/* Builds the following decl + void __tsan_func_entry (void *addr); */ + +static tree +get_func_entry_decl (void) +{ + tree typ; + static tree decl; + + if (decl != NULL) + return decl; + typ = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + decl = build_func_decl (typ, "__tsan_func_entry"); + return decl; +} + +/* Builds the following decl + void __tsan_func_exit (void); */ + +static tree +get_func_exit_decl (void) +{ + tree typ; + static tree decl; + + if (decl != NULL) + return decl; + typ = build_function_type_list (void_type_node, NULL_TREE); + decl = build_func_decl (typ, "__tsan_func_exit"); + return decl; +} + +/* Builds the following gimple sequence: + __tsan_read/writeX (&EXPR); */ + +static gimple_seq +instr_memory_access (tree expr, int is_write) +{ + tree addr_expr, expr_type, call_expr, fdecl; + gimple_seq gs; + unsigned size; + + gcc_assert (is_gimple_addressable (expr)); + addr_expr = build_addr (unshare_expr (expr), current_function_decl); + expr_type = TREE_TYPE (expr); + while (TREE_CODE (expr_type) == ARRAY_TYPE) + expr_type = TREE_TYPE (expr_type); + size = (TREE_INT_CST_LOW (TYPE_SIZE (expr_type))) / BITS_PER_UNIT; + fdecl = get_memory_access_decl (is_write, size); + call_expr = build_call_expr (fdecl, 1, addr_expr); + gs = NULL; + force_gimple_operand (call_expr, &gs, true, 0); + return gs; +} + +/* Builds the following gimple sequence: + __tsan_vptr_update (&EXPR, RHS); */ + +static gimple_seq +instr_vptr_update (tree expr, tree rhs) +{ + tree expr_ptr, call_expr, fdecl; + gimple_seq gs; + + expr_ptr = build_addr (unshare_expr (expr), current_function_decl); + fdecl = get_vptr_update_decl (); + call_expr = build_call_expr (fdecl, 2, expr_ptr, rhs); + gs = NULL; + force_gimple_operand (call_expr, &gs, true, 0); + return gs; +} + +/* Returns gimple seq that needs to be inserted at function entry. */ + +static gimple_seq +instr_func_entry (void) +{ + tree retaddr_decl, pc_addr, fdecl, call_expr; + gimple_seq gs; + + retaddr_decl = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS); + pc_addr = build_call_expr (retaddr_decl, 1, integer_zero_node); + fdecl = get_func_entry_decl (); + call_expr = build_call_expr (fdecl, 1, pc_addr); + gs = NULL; + force_gimple_operand (call_expr, &gs, true, 0); + return gs; +} + +/* Returns gimple seq that needs to be inserted before function exit. */ + +static gimple_seq +instr_func_exit (void) +{ + tree fdecl, call_expr; + gimple_seq gs; + + fdecl = get_func_exit_decl (); + call_expr = build_call_expr (fdecl, 0); + gs = NULL; + force_gimple_operand (call_expr, &gs, true, 0); + return gs; +} + +/* Sets location LOC for all gimples in the SEQ. */ + +static void +set_location (gimple_seq seq, location_t loc) +{ + gimple_seq_node n; + + for (n = gimple_seq_first (seq); n != NULL; n = n->gsbase.next) + gimple_set_location (n, loc); +} + +/* Check as to whether EXPR refers to a store to vptr. */ + +static tree +is_vptr_store (gimple stmt, tree expr, int is_write) +{ + if (is_write == 1 + && gimple_assign_single_p (stmt) + && TREE_CODE (expr) == COMPONENT_REF) + { + tree field = TREE_OPERAND (expr, 1); + if (TREE_CODE (field) == FIELD_DECL + && DECL_VIRTUAL_P (field)) + return gimple_assign_rhs1 (stmt); + } + return NULL; +} + +/* Checks as to whether EXPR refers to constant var/field/param. + Don't bother to instrument them. */ + +static int +is_load_of_const (tree expr, int is_write) +{ + if (is_write) + return 0; + if (TREE_CODE (expr) == COMPONENT_REF) + expr = TREE_OPERAND (expr, 1); + if (TREE_CODE (expr) == VAR_DECL + || TREE_CODE (expr) == PARM_DECL + || TREE_CODE (expr) == FIELD_DECL) + { + if (TREE_READONLY (expr)) + return 1; + } + return 0; +} + +/* Instruments EXPR if needed. */ + +static void +instrument_expr (gimple_stmt_iterator gsi, tree expr, int is_write) +{ + enum tree_code tcode; + unsigned fld_off, fld_size; + tree base, rhs; + gimple stmt; + gimple_seq gs; + location_t loc; + + base = get_base_address (expr); + if (base == NULL_TREE + || TREE_CODE (base) == SSA_NAME + || TREE_CODE (base) == STRING_CST) + return; + + tcode = TREE_CODE (expr); + + /* Below are things we do not instrument + (no possibility of races or not implemented yet). */ + if (/* Compiler-emitted artificial variables. */ + (DECL_P (expr) && DECL_ARTIFICIAL (expr)) + /* The var does not live in memory -> no possibility of races. */ + || (tcode == VAR_DECL + && TREE_ADDRESSABLE (expr) == 0 + && TREE_STATIC (expr) == 0) + /* Not implemented. */ + || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE + /* Not implemented. */ + || tcode == CONSTRUCTOR + /* Not implemented. */ + || tcode == PARM_DECL + /* Load of a const variable/parameter/field. */ + || is_load_of_const (expr, is_write)) + return; + + if (tcode == COMPONENT_REF) + { + tree field = TREE_OPERAND (expr, 1); + if (TREE_CODE (field) == FIELD_DECL) + { + fld_off = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field)); + fld_size = TREE_INT_CST_LOW (DECL_SIZE (field)); + if (((fld_off % BITS_PER_UNIT) != 0) + || ((fld_size % BITS_PER_UNIT) != 0)) + { + /* As of now it crashes compilation. + TODO: handle bit-fields as if touching the whole field. */ + return; + } + } + } + + /* TODO: handle other cases + (FIELD_DECL, MEM_REF, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR). */ + if (tcode != ARRAY_REF + && tcode != VAR_DECL + && tcode != COMPONENT_REF + && tcode != INDIRECT_REF + && tcode != MEM_REF) + return; + + func_mops++; + stmt = gsi_stmt (gsi); + loc = gimple_location (stmt); + rhs = is_vptr_store (stmt, expr, is_write); + if (rhs == NULL) + gs = instr_memory_access (expr, is_write); + else + gs = instr_vptr_update (expr, rhs); + set_location (gs, loc); + /* Instrumentation for assignment of a function result + must be inserted after the call. Instrumentation for + reads of function arguments must be inserted before the call. + That's because the call can contain synchronization. */ + if (is_gimple_call (stmt) && is_write) + gsi_insert_seq_after (&gsi, gs, GSI_NEW_STMT); + else + gsi_insert_seq_before (&gsi, gs, GSI_SAME_STMT); +} + +/* Instruments the gimple pointed to by GSI. */ + +static void +instrument_gimple (gimple_stmt_iterator gsi) +{ + unsigned i; + gimple stmt; + enum gimple_code gcode; + tree rhs, lhs; + + stmt = gsi_stmt (gsi); + gcode = gimple_code (stmt); + if (gcode == GIMPLE_CALL) + { + if (gimple_call_fndecl (stmt) != get_init_decl ()) + func_calls++; + } + else if (gcode == GIMPLE_ASSIGN) + { + /* Handle assignment lhs as store. */ + lhs = gimple_assign_lhs (stmt); + instrument_expr (gsi, lhs, 1); + /* Handle operands as loads. */ + for (i = 1; i < gimple_num_ops (stmt); i++) + { + rhs = gimple_op (stmt, i); + instrument_expr (gsi, rhs, 0); + } + } +} + +/* Instruments all interesting memory accesses in the current function. */ + +static void +instrument_memory_accesses (void) +{ + basic_block bb; + gimple_stmt_iterator gsi; + + FOR_EACH_BB (bb) + { + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + instrument_gimple (gsi); + } + } +} + +/* Instruments function entry. */ + +static void +instrument_func_entry (void) +{ + gimple_seq seq; + basic_block entry_bb; + edge entry_edge; + gimple_stmt_iterator gsi; + + /* Insert new BB before the first BB. */ + seq = instr_func_entry (); + gcc_assert (seq != NULL); + entry_bb = ENTRY_BLOCK_PTR; + entry_edge = single_succ_edge (entry_bb); + set_location (seq, cfun->function_start_locus); + entry_bb = split_edge (entry_edge); + gsi = gsi_start_bb (entry_bb); + gsi_insert_seq_after (&gsi, seq, GSI_NEW_STMT); +} + +/* Instruments function exits. */ + +static void +instrument_func_exit (void) +{ + location_t loc; + gimple_seq seq; + basic_block exit_bb; + gimple_stmt_iterator gsi; + gimple stmt; + edge e; + edge_iterator ei; + + /* Find all function exits. */ + exit_bb = EXIT_BLOCK_PTR; + FOR_EACH_EDGE (e, ei, exit_bb->preds) + { + gsi = gsi_last_bb (e->src); + stmt = gsi_stmt (gsi); + gcc_assert (gimple_code (stmt) == GIMPLE_RETURN); + loc = gimple_location (stmt); + seq = instr_func_exit (); + gcc_assert (seq != NULL); + set_location (seq, loc); + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); + } +} + +/* ThreadSanitizer instrumentation pass. */ + +static unsigned +tsan_pass (void) +{ + struct gimplify_ctx gctx; + + func_calls = 0; + func_mops = 0; + push_gimplify_context (&gctx); + instrument_memory_accesses (); + if (func_calls || func_mops) + { + instrument_func_entry (); + instrument_func_exit (); + } + pop_gimplify_context (NULL); + return 0; +} + +/* The pass's gate. */ + +static bool +tsan_gate (void) +{ + return flag_tsan != 0; +} + +/* Inserts __tsan_init () into the list of CTORs. */ + +void tsan_finish_file (void) +{ + tree ctor_statements; + + ctor_statements = NULL_TREE; + append_to_statement_list (build_call_expr (get_init_decl (), 0), + &ctor_statements); + cgraph_build_static_cdtor ('I', ctor_statements, + MAX_RESERVED_INIT_PRIORITY - 1); +} + +/* The pass descriptor. */ + +struct gimple_opt_pass pass_tsan = {{ + GIMPLE_PASS, + "tsan", /* name */ + tsan_gate, /* gate */ + tsan_pass, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_ssa | PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_all | TODO_update_ssa + | TODO_update_address_taken /* todo_flags_finish */ +}}; + +static bool +tsan_gate_O0 (void) +{ + return flag_tsan != 0 && !optimize; +} + +struct gimple_opt_pass pass_tsan_O0 = {{ + GIMPLE_PASS, + "tsan0", /* name */ + tsan_gate_O0, /* gate */ + tsan_pass, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_ssa | PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_all | TODO_update_ssa + | TODO_update_address_taken /* todo_flags_finish */ +}}; + Index: gcc/tsan.h =================================================================== --- gcc/tsan.h (revision 0) +++ gcc/tsan.h (revision 0) @@ -0,0 +1,26 @@ +/* ThreadSanitizer, a data race detector. + Copyright (C) 2011 Free Software Foundation, Inc. + Contributed by Dmitry Vyukov + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef TREE_TSAN +#define TREE_TSAN + +extern void tsan_finish_file (void); + +#endif /* TREE_TSAN */ Index: gcc/common.opt =================================================================== --- gcc/common.opt (revision 193016) +++ gcc/common.opt (working copy) @@ -1518,6 +1518,14 @@ fmove-loop-invariants Common Report Var(flag_move_loop_invariants) Init(1) Optimization Move loop invariant computations out of loops +ftsan +Common RejectNegative Report Var(flag_tsan) +Add ThreadSanitizer instrumentation + +ftsan-ignore= +Common RejectNegative Joined Var(flag_tsan_ignore) +-ftsan-ignore=filename ThreadSanitizer ignore file + fdce Common Var(flag_dce) Init(1) Optimization Use the RTL dead code elimination pass