From patchwork Tue Nov 6 00:37:41 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Mi X-Patchwork-Id: 197374 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 773EB2C0097 for ; Tue, 6 Nov 2012 11:37:56 +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=1352767076; h=Comment: DomainKey-Signature:Received:Received:Received:Received: MIME-Version:Received:Received:In-Reply-To:References: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=aHvwMfRkIGtC29yrXZ++9HvdylA=; b=rGfs8ORYpbwbWPVICNee+wVPabjwPLwoz5dQjuoUdusDmCxEO0HUPpxz8eTMu8 WIYeMJI9+59gZHrePAVD4YoJDuw0xhUFyrWKHoKCK01o5koYrV7pmxzWZEbumDmf TBqayDPIugHHy+9mkwiv28cNfxmg0csuqCj3rHg6EtjKA= 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:In-Reply-To:References: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=ihycK3JvW/bUKDqkId5xRBRItBotVpkFpEaj7foRefSmMjSszNSy+PPorKqLJf /Tw6GfG7JfynnkwdAs3H9o1+K50XMSKlNrOhWVPJUPlR+MZrizfwLFWImEA1HeIn fjx28olfWZGsrQ8Y4sXdnLQLcRkxYafL8eK4whhqmZ9J4=; Received: (qmail 28577 invoked by alias); 6 Nov 2012 00:37:51 -0000 Received: (qmail 28569 invoked by uid 22791); 6 Nov 2012 00:37:51 -0000 X-SWARE-Spam-Status: No, hits=-6.2 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, KHOP_THREADED, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, 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; Tue, 06 Nov 2012 00:37:42 +0000 Received: by mail-qc0-f175.google.com with SMTP id j3so4517971qcs.20 for ; Mon, 05 Nov 2012 16:37:41 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:x-system-of-record:x-gm-message-state; bh=flzulz4J549Kr5m/tlCjN03FG1eySUkJ33YCj1x8kgA=; b=g5FEVEGiZMda4y/ltomKRayfuttERbGGtI7+3EkT51w8+GGkIuEpEQSmAA6nwqnlOf w6ZvwXfPgFHYaeu7T2elJ2zPaNOMmSQA9MdOEu6fuzYiKO7RS1QPdN8TUioY18JTBIKC 6HHwnS7PO7aTxc520A1Ub3OikPQxOKbWNGMI55zq6enVFlgrwWaEuVEeUw+P08AMFg11 ggc9gdyQjArq6IWwlqarv2Wo51z6F1+gE14by+H6cEshIneBYytAvixg7yn+WSz+48uJ +hlHyk/uQTznc5I3DN+kU+RV36fpstXMfbXVpSc6VVWxtNTEjbKrphf0zyqAWe/hEfkm 2TlQ== MIME-Version: 1.0 Received: by 10.49.27.71 with SMTP id r7mr20332198qeg.38.1352162261429; Mon, 05 Nov 2012 16:37:41 -0800 (PST) Received: by 10.49.87.164 with HTTP; Mon, 5 Nov 2012 16:37:41 -0800 (PST) In-Reply-To: <20121103183906.GF1881@tucnak.redhat.com> References: <20121103183906.GF1881@tucnak.redhat.com> Date: Mon, 5 Nov 2012 16:37:41 -0800 Message-ID: Subject: Re: [tsan] ThreadSanitizer instrumentation part From: Wei Mi To: Jakub Jelinek Cc: GCC Patches , David Li , Diego Novillo , Dodji Seketeli , Dmitry Vyukov X-System-Of-Record: true X-Gm-Message-State: ALoCoQl4oxEghiOspM/MLfVNcfUNABp6LMz7bK23MGSWIVkUh8v/zlTMg2PPlLBtDrLOb44T4SGzzGupPPgCUIwoDymhF6CByOciMtDNF3XyyCVOw1Nhwoe25yn1BTufo17umNyWuqS+3S8GS+c5J8ty/HR5GjXvsKx5JMOKws+G7zYSdcNKAyo+oV4+BLtlKB+BYHPFCARW 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 Jakub, Thanks for the comments. I fix most of them except the setting of TODO_.... The new patch.txt is attached. Thanks, Wei. >> + TODO_verify_all | TODO_update_ssa > > Ideally you shouldn't need TODO_update_ssa. > I got error when I removed TODO_update_ssa, so I kept it. >> + | TODO_update_address_taken /* todo_flags_finish */ > > And why this? > If we generate tsan_read(&a) for a non-address taken static variable a, we need to change a to be address taken, right? On Sat, Nov 3, 2012 at 11:39 AM, Jakub Jelinek wrote: > On Sat, Nov 03, 2012 at 10:05:35AM -0700, Wei Mi wrote: >> --- gcc/sanitizer.def (revision 0) >> +++ gcc/sanitizer.def (revision 0) >> @@ -0,0 +1,31 @@ >> +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_16, "__tsan_write16", >> + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) >> + >> + >> + > > Please remove the trailing whitespace. Done > >> +/* Builtin used by the implementation of libsanitizer. These >> + functions are mapped to the actual implementation of the >> + libasan and libtsan library. */ >> +#undef DEF_SANITIZER_BUILTIN >> +#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ >> + DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ >> + true, true, true, ATTRS, true, flag_tsan) > > That should be eventually flag_asan || flag_tsan, as sanitizer.def > should be also for asan builtins, or it must be DEF_TSAN_BUILTIN/tsan.def. > Postpone to fix it after asan checkin to trunk. >> +static tree >> +get_memory_access_decl (bool is_write, unsigned size) >> +{ >> + enum built_in_function fcode; >> + >> + if (size <= 1) >> + fcode = is_write ? BUILT_IN_TSAN_WRITE_1 : >> + BUILT_IN_TSAN_READ_1; > > Formatting, : should be below ?. Fixed. >> + >> + return builtin_decl_implicit(fcode); > > Space before (. Several times in the code. > Fixed. > Also, as is the tsan builtins will be defined only for > C/C++ family FEs, so either something needs to be done > for other FEs, or perhaps the pass should just error out > if say the BUILT_IN_TSAN_INIT isn't defined. > Wrap builtin_decl_implicit in get_tsan_builtin_decl. If builtin_decl_implicit return invalid decl, output error message and then exit. >> +static tree >> +is_vptr_store (gimple stmt, tree expr, int is_write) > > is_write should be bool, > >> +{ >> + if (is_write == 1 > > and this just is_write > >> +static bool >> +is_load_of_const_p (tree expr, int is_write) >> +{ >> + if (is_write) >> + return false; > > Again. > Fixed >> + /* The var does not live in memory -> no possibility of races. */ >> + || (tcode == VAR_DECL >> + && !TREE_ADDRESSABLE (expr) >> + && TREE_STATIC (expr) == 0) > > Please use && !is_global_var (expr) here instead. > Changed. >> + /* TODO: handle other cases >> + (FIELD_DECL, MEM_REF, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR). */ > > The comment is obsolete, MEM_REF is handled. > Fixed. >> + if (tcode != ARRAY_REF >> + && tcode != VAR_DECL >> + && tcode != COMPONENT_REF >> + && tcode != INDIRECT_REF >> + && tcode != MEM_REF) >> + return false; >> + >> + stmt = gsi_stmt (gsi); >> + loc = gimple_location (stmt); >> + rhs = is_vptr_store (stmt, expr, is_write); >> +#ifdef DEBUG >> + if (rhs == NULL) >> + gcc_assert (is_gimple_addressable (expr)); >> +#endif > > That should be > gcc_checking_assert (rhs != NULL || is_gimple_addressable (expr)); > if you want to check it in checking versions only. > Fixed. >> + size = int_size_in_bytes(expr_type); > > Missing space. > Fixed. >> + g = gimple_build_call( >> + get_memory_access_decl(is_write, size), >> + 1, expr_ptr); > > And the formatting here is completely wrong. > Fixed. >> + } >> + else >> + g = gimple_build_call( >> + builtin_decl_implicit(BUILT_IN_TSAN_VPTR_UPDATE), >> + 1, expr_ptr); >> + gimple_set_location (g, 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) >> + { >> + int flags = gimple_call_flags (stmt); >> + /* If the call can throw, it must be the last stmt in >> + * a basicblock, so the instrumented stmts need to be >> + * inserted on a successor edge. */ > > Please avoid *'s at the beginning of comment continuation lines. > Use is_ctrl_altering_stmt (stmt) to check whether the call must > be the last stmt in a block or not. > And, don't expect there is a single_succ_edge, there could be > no edge at all (e.g. noreturn call), or there could be multiple > edges. > Fixed. Iterate every successive edge of current bb and insert stmt on each edge. >> + stmt = gsi_stmt (gsi); >> + if (is_gimple_call (stmt) && >> + (gimple_call_fndecl(stmt) != > > Again, missing spaces, && and != belong on next lines. Fixed. > >> + if (gimple_assign_single_p (stmt)) > > Not gimple_assign_load_p instead? Change to gimple_assign_load_p. >> +static bool >> +instrument_memory_accesses (void) >> +{ >> + basic_block bb; >> + gimple_stmt_iterator gsi; >> + bool fentry_exit_instrument = false; >> + >> + FOR_EACH_BB (bb) >> + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) >> + fentry_exit_instrument = instrument_gimple (gsi) || fentry_exit_instrument; > > Line too long. Just do > fentry_exit_instrument |= instrument_gimple (gsi); ? > Fixed. >> + return fentry_exit_instrument; >> +} >> + >> +/* Instruments function entry. */ >> + >> +static void >> +instrument_func_entry (void) >> +{ >> + basic_block entry_bb; >> + edge entry_edge; >> + gimple_stmt_iterator gsi; >> + tree ret_addr; >> + gimple g; >> + >> + entry_bb = ENTRY_BLOCK_PTR; >> + entry_edge = single_succ_edge (entry_bb); >> + entry_bb = split_edge (entry_edge); >> + gsi = gsi_start_bb (entry_bb); > > Why? Just add the stmts to gsi_after_labels of > single_succ (ENTRY_BLOCK_PTR) ? > Fixed. >> + >> + g = gimple_build_call( >> + builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS), >> + 1, integer_zero_node); > > Wrong formatting. > Fixed. >> + ret_addr = create_tmp_var (ptr_type_node, "ret_addr"); > > You don't need to create a decl for that, just > ret_addr = make_ssa_name (ptr_type_node, NULL); > Fixed. >> +static unsigned >> +tsan_pass (void) >> +{ >> + struct gimplify_ctx gctx; >> + >> + push_gimplify_context (&gctx); > > Why? > Removed. >> + GIMPLE_PASS, >> + "tsan0", /* name */ >> + tsan_gate_O0, /* gate */ >> + tsan_pass, /* execute */ >> + NULL, /* sub */ > > The above is clearly badly formatted, /* execute */ comment > is not aligned with others. Please just use tabs instead > of spaces. > Fixed. >> + Copyright (C) 2011 Free Software Foundation, Inc. > > We have 2012 now, so 2011, 2012. > > Jakub Fixed. Index: gcc/sanitizer.def =================================================================== --- gcc/sanitizer.def (revision 0) +++ gcc/sanitizer.def (revision 0) @@ -0,0 +1,28 @@ +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", + BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_FUNC_ENTRY, "__tsan_func_entry", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_FUNC_EXIT, "__tsan_func_exit", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_VPTR_UPDATE, "__tsan_vptr_update", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_1, "__tsan_read1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_2, "__tsan_read2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_4, "__tsan_read4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_8, "__tsan_read8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_16, "__tsan_read16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_1, "__tsan_write1", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_2, "__tsan_write2", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_4, "__tsan_write4", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_8, "__tsan_write8", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_16, "__tsan_write16", + BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) 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 +-fthread-sanitizer -fthread-sanitizer-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 -fthread-sanitizer -fthread-sanitizer-ignore +@opindex fthread-sanitizer +@opindex fthread-sanitizer-ignore +Add ThreadSanitizer instrumentation. Use @option{-fthread-sanitizer-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/tsan.c =================================================================== --- gcc/tsan.c (revision 0) +++ gcc/tsan.c (revision 0) @@ -0,0 +1,419 @@ +/* GCC instrumentation plugin for ThreadSanitizer. + Copyright (C) 2011, 2012 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 +. */ + + +#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 "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. */ + +/* Builds the following decl + void __tsan_read/writeX (void *addr); */ + +static tree +get_tsan_builtin_decl (enum built_in_function fcode) +{ + tree decl = builtin_decl_implicit (fcode); + if (decl == NULL_TREE) + internal_error ("undefined builtin %s", built_in_names[fcode]); + return decl; +} + +static tree +get_memory_access_decl (bool is_write, unsigned size) +{ + enum built_in_function fcode; + + if (size <= 1) + fcode = is_write ? BUILT_IN_TSAN_WRITE_1 + : BUILT_IN_TSAN_READ_1; + else if (size <= 3) + fcode = is_write ? BUILT_IN_TSAN_WRITE_2 + : BUILT_IN_TSAN_READ_2; + else if (size <= 7) + fcode = is_write ? BUILT_IN_TSAN_WRITE_4 + : BUILT_IN_TSAN_READ_4; + else if (size <= 15) + fcode = is_write ? BUILT_IN_TSAN_WRITE_8 + : BUILT_IN_TSAN_READ_8; + else + fcode = is_write ? BUILT_IN_TSAN_WRITE_16 + : BUILT_IN_TSAN_READ_16; + + return get_tsan_builtin_decl (fcode); +} + +/* Check as to whether EXPR refers to a store to vptr. */ + +static tree +is_vptr_store (gimple stmt, tree expr, bool is_write) +{ + if (is_write == true + && 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 bool +is_load_of_const_p (tree expr, bool is_write) +{ + if (is_write) + return false; + 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 true; + } + return false; +} + +/* Instruments EXPR if needed. If any instrumentation is inserted, + * return true. */ + +static bool +instrument_expr (gimple_stmt_iterator gsi, tree expr, bool is_write) +{ + enum tree_code tcode; + unsigned fld_off, fld_size; + tree base, rhs, expr_type, expr_ptr, builtin_decl; + basic_block bb, succ_bb; + edge_iterator ei; + edge e; + HOST_WIDE_INT size; + gimple stmt, g; + gimple_stmt_iterator start_gsi; + location_t loc; + + base = get_base_address (expr); + if (base == NULL_TREE + || TREE_CODE (base) == SSA_NAME + || TREE_CODE (base) == STRING_CST) + return false; + + 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) + && 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_p (expr, is_write)) + return false; + + 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 false; + } + } + } + + /* TODO: handle other cases + (FIELD_DECL, 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 false; + + stmt = gsi_stmt (gsi); + loc = gimple_location (stmt); + rhs = is_vptr_store (stmt, expr, is_write); + gcc_checking_assert (rhs != NULL || is_gimple_addressable (expr)); + expr_ptr = build_addr (unshare_expr (expr), + current_function_decl); + if (rhs == NULL) + { + expr_type = TREE_TYPE (expr); + while (TREE_CODE (expr_type) == ARRAY_TYPE) + expr_type = TREE_TYPE (expr_type); + size = int_size_in_bytes (expr_type); + g = gimple_build_call (get_memory_access_decl (is_write, size), + 1, expr_ptr); + } + else + { + builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_VPTR_UPDATE); + g = gimple_build_call (builtin_decl, 1, expr_ptr); + } + gimple_set_location (g, 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) + { + /* If the call can throw, it must be the last stmt in + a basicblock, so the instrumented stmts need to be + inserted in successor bbs. */ + if (is_ctrl_altering_stmt (stmt)) + { + bb = gsi_bb (gsi); + FOR_EACH_EDGE (e, ei, bb->succs) + { + succ_bb = split_edge (e); + start_gsi = gsi_start_bb (succ_bb); + gsi_insert_after (&start_gsi, g, GSI_NEW_STMT); + } + } + else + gsi_insert_after (&gsi, g, GSI_NEW_STMT); + } + else + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + return true; +} + +/* Instruments the gimple pointed to by GSI. Return + * true if func entry/exit should be instrumented. */ + +static bool +instrument_gimple (gimple_stmt_iterator gsi) +{ + gimple stmt; + tree rhs, lhs; + bool instrumented = false; + + stmt = gsi_stmt (gsi); + if (is_gimple_call (stmt) + && (gimple_call_fndecl (stmt) + != get_tsan_builtin_decl (BUILT_IN_TSAN_INIT))) + return true; + else if (is_gimple_assign (stmt)) + { + if (gimple_store_p (stmt)) + { + lhs = gimple_assign_lhs (stmt); + instrumented = instrument_expr (gsi, lhs, true); + } + if (gimple_assign_load_p (stmt)) + { + rhs = gimple_assign_rhs1 (stmt); + instrumented = instrument_expr (gsi, rhs, false); + } + } + return instrumented; +} + +/* Instruments all interesting memory accesses in the current function. + * Return true if func entry/exit should be instrumented. */ + +static bool +instrument_memory_accesses (void) +{ + basic_block bb; + gimple_stmt_iterator gsi; + bool fentry_exit_instrument = false; + + FOR_EACH_BB (bb) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + fentry_exit_instrument |= instrument_gimple (gsi); + return fentry_exit_instrument; +} + +/* Instruments function entry. */ + +static void +instrument_func_entry (void) +{ + basic_block succ_bb; + gimple_stmt_iterator gsi; + tree ret_addr, builtin_decl; + gimple g; + + succ_bb = single_succ (ENTRY_BLOCK_PTR); + gsi = gsi_after_labels (succ_bb); + + builtin_decl = get_tsan_builtin_decl (BUILT_IN_RETURN_ADDRESS); + g = gimple_build_call (builtin_decl, 1, integer_zero_node); + ret_addr = make_ssa_name (ptr_type_node, NULL); + gimple_call_set_lhs (g, ret_addr); + gimple_set_location (g, cfun->function_start_locus); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_FUNC_ENTRY); + g = gimple_build_call (builtin_decl, 1, ret_addr); + gimple_set_location (g, cfun->function_start_locus); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); +} + +/* Instruments function exits. */ + +static void +instrument_func_exit (void) +{ + location_t loc; + basic_block exit_bb; + gimple_stmt_iterator gsi; + gimple stmt, g; + tree builtin_decl; + 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); + builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_FUNC_EXIT); + g = gimple_build_call (builtin_decl, 0); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + } +} + +/* ThreadSanitizer instrumentation pass. */ + +static unsigned +tsan_pass (void) +{ + if (instrument_memory_accesses ()) + { + instrument_func_entry (); + instrument_func_exit (); + } + 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; + tree init_decl; + + ctor_statements = NULL_TREE; + init_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_INIT); + append_to_statement_list (build_call_expr (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/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}\ + %{fthread-sanitizer:-ltsan}\ %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\ %{!nostdlib:%{!nostartfiles:%E}} %{T*} }}}}}}" #endif Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 193016) +++ gcc/Makefile.in (working copy) @@ -848,7 +848,7 @@ RTL_ERROR_H = rtl-error.h $(RTL_H) $(DIA READ_MD_H = $(OBSTACK_H) $(HASHTAB_H) read-md.h PARAMS_H = params.h params.def BUILTINS_DEF = builtins.def sync-builtins.def omp-builtins.def \ - gtm-builtins.def + gtm-builtins.def sanitizer.def INTERNAL_FN_DEF = internal-fn.def INTERNAL_FN_H = internal-fn.h $(INTERNAL_FN_DEF) TREE_H = coretypes.h tree.h all-tree.def tree.def c-family/c-common.def \ @@ -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/builtins.def =================================================================== --- gcc/builtins.def (revision 193016) +++ gcc/builtins.def (working copy) @@ -149,6 +149,14 @@ along with GCC; see the file COPYING3. DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ true, true, true, ATTRS, false, flag_tm) +/* Builtin used by the implementation of libsanitizer. These + functions are mapped to the actual implementation of the + libasan and libtsan library. */ +#undef DEF_SANITIZER_BUILTIN +#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ + DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ + true, true, true, ATTRS, true, flag_tsan) + /* Define an attribute list for math functions that are normally "impure" because some of them may write into global memory for `errno'. If !flag_errno_math they are instead "const". */ @@ -825,3 +833,7 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", /* GTM builtins. */ #include "gtm-builtins.def" + +/* Sanitizer builtins. */ +#include "sanitizer.def" + 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/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 +fthread-sanitizer +Common RejectNegative Report Var(flag_tsan) +Add ThreadSanitizer instrumentation + +fthread-sanitizer-ignore= +Common RejectNegative Joined Var(flag_tsan_ignore) +-fthread-sanitizer-ignore=filename ThreadSanitizer ignore file + fdce Common Var(flag_dce) Init(1) Optimization Use the RTL dead code elimination pass 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);