public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: [tsan] ThreadSanitizer instrumentation part
@ 2012-11-01  6:00 Xinliang David Li
  2012-11-01  6:27 ` Jakub Jelinek
       [not found] ` <CA+4CFy4uuCtFuiqai0b_VXib1te=DeBv1_EWDFjf8UP6MYMJPg@mail.gmail.com>
  0 siblings, 2 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01  6:00 UTC (permalink / raw)
  To: Wei Mi
  Cc: GCC Patches, Dmitry Vyukov, Diego Novillo, Jakub Jelinek, Dodji Seketeli

> --fno-default-inline @gol
> +-fmove-loop-invariants -fmudflap -fmudflapir -fmudflapth -fno-branch-count-reg @gol
> +-ftsan -ftsan-ignore -fno-default-inline @gol

Change -ftsan to -fthread-sanitizer

>  -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 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.
> +

-ftsan --> -fthread-sanitizer

> + *
> + * 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/
> + /

You can copy this part of header from asan.c


> +static int func_calls;
> +
> +/* Returns a definition of a runtime functione with type TYP and name NAME.  */


s/functione/function/

> +
> +static tree
> +build_func_decl (tree typ, const char *name)
> +/* Builds the following decl
> +   void __tsan_read/writeX (void *addr);  */
> +
> +static tree
> +get_memory_access_decl (int is_write, unsigned size)

Change is_write to type bool.


> +{
> +  tree typ, *decl;
> +  char fname [64];
> +  static tree cache [2][17];

There is no need to make this function local. define a macro for value 17.
> +
> +  is_write = !!is_write;


No need for this after making is_write bool.


> +  if (size <= 1)
> +    size = 1;
> +  else if (size <= 3)
> +    size = 2;
> +  else if (size <= 7)
> +    size = 4;


Missing function comment:

/* This function returns the function decl for __tsan_init.  */

> +static tree
> +get_init_decl (void)
> +{
> +  tree typ;
> +  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;


Use gimple creator API: gimple_build_call --> see examples in tree-profile.c.

Return the gimple_stmt_iterator for the newly created call stmt.


> +}
> +
> +/* 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;
> +}


Same as above -- directly use gimple_build_call interface.


> +
> +/* 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;
> +}


Same as above.


> +
> +/* 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;
> +}
> +


Same as above.


> +/* 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);
> +}

Is there a need for this function? The location can be passed into the
creator functions.


> +
> +/* 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

change int --> bool

> +is_load_of_const (tree expr, int is_write)

Better name: is_load_of_const_p (...)

> +{
> +  if (is_write)
> +    return 0;

returns 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 1;

returns true;

> +    }
> +  return 0;

returns false;


> +      || 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_ADDRESSABLE (expr)

> +         && TREE_STATIC (expr) == 0)


To detect stack varaible, TREE_STATIC can not be used.  Use
!DECL_EXTERNAL instead.


> +      /* Not implemented.  */
> +      || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE

> +    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);

Pass the location to the instrumetors.

> +  set_location (gs, loc);

Remove this.

> +  /* 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);
> +}
> +

> +}
> +
> +
> +void tsan_finish_file (void)

function name starts a new line.

void
tsan_finish_file

> +{
> +  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.  */


David


On Wed, Oct 31, 2012 at 11:34 AM, Wei Mi <wmi@google.com> wrote:
> 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  <wmi@gmail.com>
>
>         * 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.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01  6:00 [tsan] ThreadSanitizer instrumentation part Xinliang David Li
@ 2012-11-01  6:27 ` Jakub Jelinek
  2012-11-01 15:47   ` Xinliang David Li
       [not found] ` <CA+4CFy4uuCtFuiqai0b_VXib1te=DeBv1_EWDFjf8UP6MYMJPg@mail.gmail.com>
  1 sibling, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01  6:27 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Wei Mi, GCC Patches, Dmitry Vyukov, Diego Novillo, Dodji Seketeli

On Wed, Oct 31, 2012 at 11:00:17PM -0700, Xinliang David Li wrote:
> > +  /* 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_ADDRESSABLE (expr)
> 
> > +         && TREE_STATIC (expr) == 0)
> 
> 
> To detect stack varaible, TREE_STATIC can not be used.  Use
> !DECL_EXTERNAL instead.

TREE_STATIC is right for that.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01  6:27 ` Jakub Jelinek
@ 2012-11-01 15:47   ` Xinliang David Li
  0 siblings, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 15:47 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Wei Mi, GCC Patches, Dmitry Vyukov, Diego Novillo, Dodji Seketeli

On Wed, Oct 31, 2012 at 11:27 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Oct 31, 2012 at 11:00:17PM -0700, Xinliang David Li wrote:
>> > +  /* 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_ADDRESSABLE (expr)
>>
>> > +         && TREE_STATIC (expr) == 0)
>>
>>
>> To detect stack varaible, TREE_STATIC can not be used.  Use
>> !DECL_EXTERNAL instead.
>
> TREE_STATIC is right for that.

Hmm, what does this condition try to capture then?

David

>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]     ` <CA+4CFy7ecGOkmXsdkC=LbSK5KMdBSR6eT0oLmfn2nbyr0S2bMA@mail.gmail.com>
@ 2012-11-03  1:31       ` Wei Mi
  2012-11-03 17:05         ` Wei Mi
  0 siblings, 1 reply; 56+ messages in thread
From: Wei Mi @ 2012-11-03  1:31 UTC (permalink / raw)
  To: GCC Patches
  Cc: David Li, Jakub Jelinek, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 8556 bytes --]

Hi,

Thanks for so many useful comments! I update the file according to the
comments. The major changes include adding sanitizer.def and
generating gimple directly. New patch file is attached.

> On Wed, Oct 31, 2012 at 11:34:10AM -0700, Wei Mi wrote:
>> gcc/ChangeLog:
>> 2012-10-31  Wei Mi  <wmi@gmail.com>
>
> If Dmitry wrote parts of the patch, it would be nice to mention
> him in the ChangeLog too.

> All ChangeLog entries should end with a dot.

Changed.

2012-10-31  Dmitry Vyukov  <dvyukov@google.com>
                     Wei Mi  <wmi@google.com>

        * Makefile.in (tsan.o): New.
        (BUILTINS_DEF): Add sanitizer.def.
        * sanitizer.def: 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
        -fthread_sanitizer is on.
        * tsan.c: New file about tsan.
        * tsan.h: Ditto.


>>  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 */
>
> Why this?  Simply don't include that header in tsan.c, it is already
> included by basic-block.h.

Remove cfghooks.h from tsan.c. Remove the #ifdef GCC_CFGHOOKS_H from cfghooks.h

> Can't google just assign the code to FSF, and use a standard boilerplate
> as everything else in gcc/ ?

Copy from asan header and make some change.

>> +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;
>> +}
> ...
>
> Instead of this (but same applies to asan), I think we should just consider
> putting it into builtins.def (or have sanitizer.def like there is sync.def
> or omp-builtins.def).  The problem might be non-C/C++ family frontends
> though.

Create sanitizer.def and use builtin_decl_implicit to create builtin decls.

>> +  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;
>
> int_size_in_bytes.

Changed.

> preferrably without building everything as trees, then gimplifying it.

Generate gimple directly. Remove funcs: instr_memory_access,
instr_vptr_update, instr_func_entry, instr_func_exit

> For func_calls and func_mops, I believe why you need two variables instead
> of just one, and why the function can't just return a bool whether
> entry/exit needs to be instrumented or not.

instrument_memory_accesses return a bool indicating whether or not
entry/exit needs to be instrumented. func_calls and func_mops removed.

>> +set_location (gimple_seq seq, location_t loc)
>> +{
>> +  gimple_seq_node n;
>> +
>> +  for (n = gimple_seq_first (seq); n != NULL; n = n->gsbase.next)
>
> This really should use a stmt iterator.

set_location removed. set gimple location using gimple_set_location
everytime a new gimple statement is inserted.

>> +  FOR_EACH_BB (bb)
>> +    {
>> +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>> +        {
>> +          instrument_gimple (gsi);
>> +        }
>> +    }
>
> Extraneous two pairs of {}s.

Fixed.

>> +struct gimple_opt_pass pass_tsan = {{
>
> Please watch formatting of other gimple_opt_pass structures.
> {{ isn't used anywhere.

Fixed.

> Is that the option that LLVM uses (I'm talking about -faddress-sanitizer
> in LLVM vs. -fasan right now in GCC, isn't that similar?).

Fixed.

> +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;
> +}
>
> The above can crash the compiler btw, as that static tree decl
> (in many other functions) is not GTY(()) marked (must be file scope for
> that), thus ggc_collect might free it.  Also, please use type
> instead of typ for variable names.

Func get_init_decl removed after generating gimple directly.

>> +  /* 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);
>
> Inserting stmts after a call may or may not work.  E.g. if the call
> can throw, it must be the last stmt in a basic block, so then the
> stmts need to be inserted on a successor edge.  Similarly noreturn
> call must be last (but in that case it shouldn't have lhs).

Add processing for call which can throw.
  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. */
      if (!(flags & ECF_NOTHROW))
        {
          bb = gsi_bb(gsi);
          succ_edge = single_succ_edge (bb);
          succ_bb = split_edge (succ_edge);
          gsi = gsi_start_bb (succ_bb);
        }
      gsi_insert_after (&gsi, g, GSI_NEW_STMT);
    }
  else
    gsi_insert_before (&gsi, g, GSI_SAME_STMT);

>> +  gcode = gimple_code (stmt);
>> +  if (gcode == GIMPLE_CALL)
>
> is_gimple_call (stmt)

Fixed.

>> +  else if (gcode == GIMPLE_ASSIGN)
>
> is_gimple_assign (stmt)

Fixed.

>> +    {
>> +      /* Handle assignment lhs as store.  */
>> +      lhs = gimple_assign_lhs (stmt);
>> +      instrument_expr (gsi, lhs, 1);
>
> To find what a store or load is, you can just use the new
> gimple_store_p (stmt) and gimple_assign_load_p (stmt)
> predicates, or at least just do gimple_assign_single_p (stmt)
> to guard instrument_expr calls on both lhs and rhs1.
> No need to scan all operands, only single rhs assignments
> can be loads.

Fixed:
  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_single_p (stmt))
        {
          rhs = gimple_assign_rhs1 (stmt);
          instrumented = instrument_expr (gsi, rhs, false);
        }
    }

>> +static int func_calls;
>> +
>> +/* Returns a definition of a runtime functione with type TYP and name NAME.  */
>
>
> s/functione/function/

Fixed.

>> +static tree
>> +get_memory_access_decl (int is_write, unsigned size)
>
> Change is_write to type bool.

Fixed.

>> +  tree typ, *decl;
>> +  char fname [64];
>> +  static tree cache [2][17];
>
> There is no need to make this function local. define a macro for value 17.

The cache is removed after we use builtin_decl_implicit to create builtin decls.

>> +  is_write = !!is_write;
>
>
> No need for this after making is_write bool.

Fixed.

> Missing function comment:
>
> /* This function returns the function decl for __tsan_init.  */
>
>> +static tree
>> +get_init_decl (void)
>> +{
>> +  tree typ;
>> +  return decl;
>> +}
>> +

The func is removed.

>> +  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;
>
>
> Use gimple creator API: gimple_build_call --> see examples in tree-profile.c.
>
> Return the gimple_stmt_iterator for the newly created call stmt.

Fixed.

>> +        return 1;
>
> returns true;
>
>> +    }
>> +  return 0;
>
> returns false;

Fixed.

>> +is_load_of_const (tree expr, int is_write)
>
> Better name: is_load_of_const_p (...)

Fixed.

>> +void tsan_finish_file (void)
>
> function name starts a new line.

Fixed.

Thanks,
Wei.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 23152 bytes --]

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
 <http://www.gnu.org/licenses/>.  */
 
+#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 <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#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

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-03  1:31       ` Wei Mi
@ 2012-11-03 17:05         ` Wei Mi
  2012-11-03 18:39           ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Wei Mi @ 2012-11-03 17:05 UTC (permalink / raw)
  To: GCC Patches
  Cc: David Li, Jakub Jelinek, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 9097 bytes --]

Sorry, I attached an incorrect patch.txt yesterday. This is the correct one.

Thanks,
Wei.

On Fri, Nov 2, 2012 at 6:31 PM, Wei Mi <wmi@google.com> wrote:
> Hi,
>
> Thanks for so many useful comments! I update the file according to the
> comments. The major changes include adding sanitizer.def and
> generating gimple directly. New patch file is attached.
>
>> On Wed, Oct 31, 2012 at 11:34:10AM -0700, Wei Mi wrote:
>>> gcc/ChangeLog:
>>> 2012-10-31  Wei Mi  <wmi@gmail.com>
>>
>> If Dmitry wrote parts of the patch, it would be nice to mention
>> him in the ChangeLog too.
>
>> All ChangeLog entries should end with a dot.
>
> Changed.
>
> 2012-10-31  Dmitry Vyukov  <dvyukov@google.com>
>                      Wei Mi  <wmi@google.com>
>
>         * Makefile.in (tsan.o): New.
>         (BUILTINS_DEF): Add sanitizer.def.
>         * sanitizer.def: 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
>         -fthread_sanitizer is on.
>         * tsan.c: New file about tsan.
>         * tsan.h: Ditto.
>
>
>>>  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 */
>>
>> Why this?  Simply don't include that header in tsan.c, it is already
>> included by basic-block.h.
>
> Remove cfghooks.h from tsan.c. Remove the #ifdef GCC_CFGHOOKS_H from cfghooks.h
>
>> Can't google just assign the code to FSF, and use a standard boilerplate
>> as everything else in gcc/ ?
>
> Copy from asan header and make some change.
>
>>> +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;
>>> +}
>> ...
>>
>> Instead of this (but same applies to asan), I think we should just consider
>> putting it into builtins.def (or have sanitizer.def like there is sync.def
>> or omp-builtins.def).  The problem might be non-C/C++ family frontends
>> though.
>
> Create sanitizer.def and use builtin_decl_implicit to create builtin decls.
>
>>> +  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;
>>
>> int_size_in_bytes.
>
> Changed.
>
>> preferrably without building everything as trees, then gimplifying it.
>
> Generate gimple directly. Remove funcs: instr_memory_access,
> instr_vptr_update, instr_func_entry, instr_func_exit
>
>> For func_calls and func_mops, I believe why you need two variables instead
>> of just one, and why the function can't just return a bool whether
>> entry/exit needs to be instrumented or not.
>
> instrument_memory_accesses return a bool indicating whether or not
> entry/exit needs to be instrumented. func_calls and func_mops removed.
>
>>> +set_location (gimple_seq seq, location_t loc)
>>> +{
>>> +  gimple_seq_node n;
>>> +
>>> +  for (n = gimple_seq_first (seq); n != NULL; n = n->gsbase.next)
>>
>> This really should use a stmt iterator.
>
> set_location removed. set gimple location using gimple_set_location
> everytime a new gimple statement is inserted.
>
>>> +  FOR_EACH_BB (bb)
>>> +    {
>>> +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>>> +        {
>>> +          instrument_gimple (gsi);
>>> +        }
>>> +    }
>>
>> Extraneous two pairs of {}s.
>
> Fixed.
>
>>> +struct gimple_opt_pass pass_tsan = {{
>>
>> Please watch formatting of other gimple_opt_pass structures.
>> {{ isn't used anywhere.
>
> Fixed.
>
>> Is that the option that LLVM uses (I'm talking about -faddress-sanitizer
>> in LLVM vs. -fasan right now in GCC, isn't that similar?).
>
> Fixed.
>
>> +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;
>> +}
>>
>> The above can crash the compiler btw, as that static tree decl
>> (in many other functions) is not GTY(()) marked (must be file scope for
>> that), thus ggc_collect might free it.  Also, please use type
>> instead of typ for variable names.
>
> Func get_init_decl removed after generating gimple directly.
>
>>> +  /* 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);
>>
>> Inserting stmts after a call may or may not work.  E.g. if the call
>> can throw, it must be the last stmt in a basic block, so then the
>> stmts need to be inserted on a successor edge.  Similarly noreturn
>> call must be last (but in that case it shouldn't have lhs).
>
> Add processing for call which can throw.
>   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. */
>       if (!(flags & ECF_NOTHROW))
>         {
>           bb = gsi_bb(gsi);
>           succ_edge = single_succ_edge (bb);
>           succ_bb = split_edge (succ_edge);
>           gsi = gsi_start_bb (succ_bb);
>         }
>       gsi_insert_after (&gsi, g, GSI_NEW_STMT);
>     }
>   else
>     gsi_insert_before (&gsi, g, GSI_SAME_STMT);
>
>>> +  gcode = gimple_code (stmt);
>>> +  if (gcode == GIMPLE_CALL)
>>
>> is_gimple_call (stmt)
>
> Fixed.
>
>>> +  else if (gcode == GIMPLE_ASSIGN)
>>
>> is_gimple_assign (stmt)
>
> Fixed.
>
>>> +    {
>>> +      /* Handle assignment lhs as store.  */
>>> +      lhs = gimple_assign_lhs (stmt);
>>> +      instrument_expr (gsi, lhs, 1);
>>
>> To find what a store or load is, you can just use the new
>> gimple_store_p (stmt) and gimple_assign_load_p (stmt)
>> predicates, or at least just do gimple_assign_single_p (stmt)
>> to guard instrument_expr calls on both lhs and rhs1.
>> No need to scan all operands, only single rhs assignments
>> can be loads.
>
> Fixed:
>   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_single_p (stmt))
>         {
>           rhs = gimple_assign_rhs1 (stmt);
>           instrumented = instrument_expr (gsi, rhs, false);
>         }
>     }
>
>>> +static int func_calls;
>>> +
>>> +/* Returns a definition of a runtime functione with type TYP and name NAME.  */
>>
>>
>> s/functione/function/
>
> Fixed.
>
>>> +static tree
>>> +get_memory_access_decl (int is_write, unsigned size)
>>
>> Change is_write to type bool.
>
> Fixed.
>
>>> +  tree typ, *decl;
>>> +  char fname [64];
>>> +  static tree cache [2][17];
>>
>> There is no need to make this function local. define a macro for value 17.
>
> The cache is removed after we use builtin_decl_implicit to create builtin decls.
>
>>> +  is_write = !!is_write;
>>
>>
>> No need for this after making is_write bool.
>
> Fixed.
>
>> Missing function comment:
>>
>> /* This function returns the function decl for __tsan_init.  */
>>
>>> +static tree
>>> +get_init_decl (void)
>>> +{
>>> +  tree typ;
>>> +  return decl;
>>> +}
>>> +
>
> The func is removed.
>
>>> +  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;
>>
>>
>> Use gimple creator API: gimple_build_call --> see examples in tree-profile.c.
>>
>> Return the gimple_stmt_iterator for the newly created call stmt.
>
> Fixed.
>
>>> +        return 1;
>>
>> returns true;
>>
>>> +    }
>>> +  return 0;
>>
>> returns false;
>
> Fixed.
>
>>> +is_load_of_const (tree expr, int is_write)
>>
>> Better name: is_load_of_const_p (...)
>
> Fixed.
>
>>> +void tsan_finish_file (void)
>>
>> function name starts a new line.
>
> Fixed.
>
> Thanks,
> Wei.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 24108 bytes --]

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/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/sanitizer.def
===================================================================
--- gcc/sanitizer.def	(revision 0)
+++ gcc/sanitizer.def	(revision 0)
@@ -0,0 +1,31 @@
+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/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/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/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/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/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/tsan.c
===================================================================
--- gcc/tsan.c	(revision 0)
+++ gcc/tsan.c	(revision 0)
@@ -0,0 +1,416 @@
+/* GCC instrumentation plugin for ThreadSanitizer. 
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<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 "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_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 builtin_decl_implicit(fcode);
+}
+
+/* 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 bool 
+is_load_of_const_p (tree expr, int 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;
+  basic_block bb, succ_bb;
+  edge succ_edge;
+  HOST_WIDE_INT size;
+  gimple stmt, g;
+  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, 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 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
+  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
+    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. */
+      if (!(flags & ECF_NOTHROW)) 
+        {
+          bb = gsi_bb(gsi); 
+          succ_edge = single_succ_edge (bb);
+          succ_bb = split_edge (succ_edge);
+          gsi = gsi_start_bb (succ_bb);
+        }
+      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) != 
+       builtin_decl_implicit(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_single_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) || fentry_exit_instrument;
+  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);
+
+  g = gimple_build_call(
+        builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS),
+        1, integer_zero_node);
+  ret_addr = create_tmp_var (ptr_type_node, "ret_addr");
+  gimple_call_set_lhs (g, ret_addr);
+  gimple_set_location(g, cfun->function_start_locus);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+
+  g = gimple_build_call(
+        builtin_decl_implicit(BUILT_IN_TSAN_FUNC_ENTRY), 
+        1, ret_addr); 
+  gimple_set_location(g, cfun->function_start_locus);
+  gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+}
+
+/* Instruments function exits.  */
+
+static void
+instrument_func_exit (void)
+{
+  location_t loc;
+  basic_block exit_bb;
+  gimple_stmt_iterator gsi;
+  gimple stmt, g;
+  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);
+      g = gimple_build_call(
+            builtin_decl_implicit(BUILT_IN_TSAN_FUNC_EXIT), 0);
+      gimple_set_location (g, loc);
+      gsi_insert_before(&gsi, g, GSI_SAME_STMT);
+    }
+}
+
+/* ThreadSanitizer instrumentation pass.  */
+
+static unsigned
+tsan_pass (void)
+{
+  struct gimplify_ctx gctx;
+
+  push_gimplify_context (&gctx);
+  if (instrument_memory_accesses ())
+    {
+      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;
+  tree init_decl;
+
+  ctor_statements = NULL_TREE;
+  init_decl = builtin_decl_implicit(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 <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef TREE_TSAN
+#define TREE_TSAN
+
+extern void tsan_finish_file (void);
+
+#endif /* TREE_TSAN */

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-03 17:05         ` Wei Mi
@ 2012-11-03 18:39           ` Jakub Jelinek
  2012-11-06  0:37             ` Wei Mi
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-03 18:39 UTC (permalink / raw)
  To: Wei Mi
  Cc: GCC Patches, David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

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.

> +/* 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.

> +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 ?.
> +
> +  return builtin_decl_implicit(fcode);

Space before (. Several times in the code.

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.

> +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.

> +      /* 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.

> +  /* TODO: handle other cases
> +     (FIELD_DECL, MEM_REF, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */

The comment is obsolete, MEM_REF is handled.

> +  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.

> +      size = int_size_in_bytes(expr_type);

Missing space.

> +      g = gimple_build_call(
> +            get_memory_access_decl(is_write, size),
> +            1, expr_ptr);

And the formatting here is completely wrong.

> +    }
> +  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.

> +  stmt = gsi_stmt (gsi);
> +  if (is_gimple_call (stmt) && 
> +      (gimple_call_fndecl(stmt) != 

Again, missing spaces, && and != belong on next lines.

> +      if (gimple_assign_single_p (stmt))

Not gimple_assign_load_p instead?
> +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); ?

> +  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) ?

> +
> +  g = gimple_build_call(
> +        builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS),
> +        1, integer_zero_node);

Wrong formatting.

> +  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);

> +static unsigned
> +tsan_pass (void)
> +{
> +  struct gimplify_ctx gctx;
> +
> +  push_gimplify_context (&gctx);

Why?

> +  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.

> +  TODO_verify_all | TODO_update_ssa

Ideally you shouldn't need TODO_update_ssa.

> +    | TODO_update_address_taken /* todo_flags_finish  */

And why this?

> +   Copyright (C) 2011 Free Software Foundation, Inc.

We have 2012 now, so 2011, 2012.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-03 18:39           ` Jakub Jelinek
@ 2012-11-06  0:37             ` Wei Mi
  2012-11-12 18:58               ` Wei Mi
  2012-11-13 16:41               ` Jakub Jelinek
  0 siblings, 2 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-06  0:37 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 7184 bytes --]

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 <jakub@redhat.com> 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.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 24372 bytes --]

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 <dvyukov@google.com>
+
+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
+<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 "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 <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#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);

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-06  0:37             ` Wei Mi
@ 2012-11-12 18:58               ` Wei Mi
  2012-11-13 16:41               ` Jakub Jelinek
  1 sibling, 0 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-12 18:58 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

For TODO_update_ssa, when we insert tsan_write(&a), current function's
ssa_renaming_needed flag will be set in finalize_ssa_defs because we
insert non-ssaname vdef. An assertion in execute_todo will check
whether we have TODO_update_ssa set when current function's
ssa_renaming_needed flag is set. That is why I will get assertion when
I remove TODO_update_ssa flag.

Is it ok to keep TODO_update_ssa and TODO_update_address_taken?

Thanks,
Wei.

On Mon, Nov 5, 2012 at 4:37 PM, Wei Mi <wmi@google.com> wrote:
> 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 <jakub@redhat.com> 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.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-06  0:37             ` Wei Mi
  2012-11-12 18:58               ` Wei Mi
@ 2012-11-13 16:41               ` Jakub Jelinek
  2012-11-13 17:25                 ` Xinliang David Li
  2012-11-14  0:08                 ` Wei Mi
  1 sibling, 2 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-13 16:41 UTC (permalink / raw)
  To: Wei Mi
  Cc: GCC Patches, David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Mon, Nov 05, 2012 at 04:37:41PM -0800, Wei Mi wrote:
> Thanks for the comments. I fix most of them except the setting of
> TODO_.... The new patch.txt is attached.

Please update the patch against trunk, it doesn't apply cleanly because
of the asan commit.  I took the liberty to do at least some formatting
cleanups and other small tweaks against your patch to tsan.c.

> >> +  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.

If it were just for the instrumentations, then you can easily update
the vdef/vuse yourself, e.g. when inserting before a memory write,
you can copy over the gimple_vuse of that write to gimple_vuse of the
instrumentation call, create a new SSA_NAME for the gimple_vdef and
and set the write's gimple_vuse to that new SSA_NAME.  For call
instrumentation it would be a tiny bit harder, but for the instrumentation
of function entry/exit you'd need to find out the current vop at that point.
So perhaps we can live with that, at least for now.

> >> +    | 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?

That is complete misunderstanding of what update_address_taken does.
It removes TREE_ADDRESSABLE from addressables that are no longer
addressable, rather than adding TREE_ADDRESSABLE bits.  For the latter
there is mark_addressable function.
> 
> Wrap builtin_decl_implicit in get_tsan_builtin_decl. If
> builtin_decl_implicit return invalid decl, output error message and
> then exit.

I've moved that over just to the gate, eventually there should be a routine
that will initialize the builtins if they aren't by the FE.

> > 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.

But wrongly, for one adding the same stmt to multiple basic blocks
is going to crash terribly, but also you IMHO want to insert just
on fallthru edge, if the routine throws or longjmps, the result isn't
written.

--- gcc/tsan.c.jj	2012-11-13 16:46:21.000000000 +0100
+++ gcc/tsan.c	2012-11-13 17:22:56.054837754 +0100
@@ -1,4 +1,4 @@
-/* GCC instrumentation plugin for ThreadSanitizer. 
+/* GCC instrumentation plugin for ThreadSanitizer.
    Copyright (C) 2011, 2012 Free Software Foundation, Inc.
    Contributed by Dmitry Vyukov <dvyukov@google.com>
 
@@ -44,36 +44,27 @@ along with GCC; see the file COPYING3.
    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;
+		     : BUILT_IN_TSAN_READ_1;
   else if (size <= 3)
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_2 
-                     : BUILT_IN_TSAN_READ_2;
+    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;
+    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;
+    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;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE_16
+		     : BUILT_IN_TSAN_READ_16;
 
-  return get_tsan_builtin_decl (fcode);
+  return builtin_decl_implicit (fcode);
 }
 
 /* Check as to whether EXPR refers to a store to vptr.  */
@@ -81,14 +72,14 @@ get_memory_access_decl (bool is_write, u
 static tree
 is_vptr_store (gimple stmt, tree expr, bool is_write)
 {
-  if (is_write == true 
+  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);
+	  && DECL_VIRTUAL_P (field))
+	return gimple_assign_rhs1 (stmt);
     }
   return NULL;
 }
@@ -96,7 +87,7 @@ is_vptr_store (gimple stmt, tree expr, b
 /* Checks as to whether EXPR refers to constant var/field/param.
    Don't bother to instrument them.  */
 
-static bool 
+static bool
 is_load_of_const_p (tree expr, bool is_write)
 {
   if (is_write)
@@ -108,7 +99,7 @@ is_load_of_const_p (tree expr, bool is_w
       || TREE_CODE (expr) == FIELD_DECL)
     {
       if (TREE_READONLY (expr))
-        return true;
+	return true;
     }
   return false;
 }
@@ -116,18 +107,14 @@ is_load_of_const_p (tree expr, bool is_w
 /* Instruments EXPR if needed. If any instrumentation is inserted,
  * return true. */
 
-static bool 
+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;
+  basic_block bb;
   HOST_WIDE_INT size;
   gimple stmt, g;
-  gimple_stmt_iterator start_gsi;
   location_t loc;
 
   base = get_base_address (expr);
@@ -144,8 +131,8 @@ instrument_expr (gimple_stmt_iterator gs
       (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)
+	  && !TREE_ADDRESSABLE (expr)
+	  && TREE_STATIC (expr) == 0)
       /* Not implemented.  */
       || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE
       /* Not implemented.  */
@@ -156,22 +143,21 @@ instrument_expr (gimple_stmt_iterator gs
       || 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;
-            }
-        }
-    }
+  size = int_size_in_bytes (TREE_TYPE (expr));
+  if (size == -1)
+    return false;
+
+  /* For now just avoid instrumenting bit field acceses.
+     TODO: handle bit-fields as if touching the whole field.  */
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  get_inner_reference (expr, &bitsize, &bitpos, &offset,
+		       &mode, &unsignedp, &volatilep, false);
+  if (bitpos % (size * BITS_PER_UNIT)
+      || bitsize != size * BITS_PER_UNIT)
+    return false;
 
   /* TODO: handle other cases
      (FIELD_DECL, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */
@@ -186,44 +172,42 @@ instrument_expr (gimple_stmt_iterator gs
   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);
+  expr_ptr = build_fold_addr_expr (unshare_expr (expr));
   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); 
+	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);
+			     1, expr_ptr);
     }
   else
     {
-      builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_VPTR_UPDATE);
+      builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_VPTR_UPDATE);
       g = gimple_build_call (builtin_decl, 1, expr_ptr);
     }
-  gimple_set_location (g, loc); 
+  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 (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);
-            }
-        }
+	 a basic block, so the instrumented stmts need to be
+	 inserted in successor bbs. */
+      if (is_ctrl_altering_stmt (stmt))
+	{
+	  edge e;
+
+	  bb = gsi_bb (gsi);
+	  e = find_fallthru_edge (bb->succs);
+	  if (e)
+	    gsi_insert_seq_on_edge_immediate (e, g);
+	}
       else
-        gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+	gsi_insert_after (&gsi, g, GSI_NEW_STMT);
     }
   else
     gsi_insert_before (&gsi, g, GSI_SAME_STMT);
@@ -242,22 +226,22 @@ instrument_gimple (gimple_stmt_iterator
   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; 
+  if (is_gimple_call (stmt)
+      && (gimple_call_fndecl (stmt)
+	  != builtin_decl_implicit (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);
-        }
+	{
+	  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);
-        }
+	{
+	  rhs = gimple_assign_rhs1 (stmt);
+	  instrumented = instrument_expr (gsi, rhs, false);
+	}
     }
   return instrumented;
 }
@@ -265,7 +249,7 @@ instrument_gimple (gimple_stmt_iterator
 /* Instruments all interesting memory accesses in the current function.
  * Return true if func entry/exit should be instrumented. */
 
-static bool 
+static bool
 instrument_memory_accesses (void)
 {
   basic_block bb;
@@ -291,15 +275,15 @@ instrument_func_entry (void)
   succ_bb = single_succ (ENTRY_BLOCK_PTR);
   gsi = gsi_after_labels (succ_bb);
 
-  builtin_decl = get_tsan_builtin_decl (BUILT_IN_RETURN_ADDRESS);
+  builtin_decl = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
   g = gimple_build_call (builtin_decl, 1, integer_zero_node);
-  ret_addr = make_ssa_name (ptr_type_node, NULL); 
+  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); 
+  builtin_decl =  builtin_decl_implicit (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);
 }
@@ -325,7 +309,7 @@ instrument_func_exit (void)
       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);
+      builtin_decl = builtin_decl_implicit (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);
@@ -350,70 +334,71 @@ tsan_pass (void)
 static bool
 tsan_gate (void)
 {
-  return flag_tsan != 0;
+  return flag_tsan != 0 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
 }
 
 /* Inserts __tsan_init () into the list of CTORs.  */
 
-void 
+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); 
+  init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
   append_to_statement_list (build_call_expr (init_decl, 0),
-                            &ctor_statements);
+			    &ctor_statements);
   cgraph_build_static_cdtor ('I', ctor_statements,
-                             MAX_RESERVED_INIT_PRIORITY - 1);
+			     MAX_RESERVED_INIT_PRIORITY - 1);
 }
 
 /* The pass descriptor.  */
 
-struct gimple_opt_pass pass_tsan = 
+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  */
+  "tsan",				/* name  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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  */
+  | TODO_update_address_taken		/* todo_flags_finish  */
  }
 };
 
-static bool                             
+static bool			    
 tsan_gate_O0 (void)
-{ 
-  return flag_tsan != 0 && !optimize;   
-} 
+{
+  return flag_tsan != 0 && !optimize;  
+}
 
-struct gimple_opt_pass pass_tsan_O0 = 
+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  */
+  "tsan0",				/* name  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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  */
+  | TODO_update_address_taken		/* todo_flags_finish  */
  }
 };
-


	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-13 16:41               ` Jakub Jelinek
@ 2012-11-13 17:25                 ` Xinliang David Li
  2012-11-13 17:36                   ` Jakub Jelinek
  2012-11-14  0:08                 ` Wei Mi
  1 sibling, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-13 17:25 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Tue, Nov 13, 2012 at 8:40 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Nov 05, 2012 at 04:37:41PM -0800, Wei Mi wrote:
>> Thanks for the comments. I fix most of them except the setting of
>> TODO_.... The new patch.txt is attached.
>
> Please update the patch against trunk, it doesn't apply cleanly because
> of the asan commit.  I took the liberty to do at least some formatting
> cleanups and other small tweaks against your patch to tsan.c.
>
>> >> +  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.
>
> If it were just for the instrumentations, then you can easily update
> the vdef/vuse yourself, e.g. when inserting before a memory write,
> you can copy over the gimple_vuse of that write to gimple_vuse of the
> instrumentation call, create a new SSA_NAME for the gimple_vdef and
> and set the write's gimple_vuse to that new SSA_NAME.  For call
> instrumentation it would be a tiny bit harder, but for the instrumentation
> of function entry/exit you'd need to find out the current vop at that point.
> So perhaps we can live with that, at least for now.
>
>> >> +    | 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?
>
> That is complete misunderstanding of what update_address_taken does.
> It removes TREE_ADDRESSABLE from addressables that are no longer
> addressable, rather than adding TREE_ADDRESSABLE bits.

It will do the latter too. See iv-opts.

>  For the latter
> there is mark_addressable function.

This is certainly cheaper to use.

David



>>
>> Wrap builtin_decl_implicit in get_tsan_builtin_decl. If
>> builtin_decl_implicit return invalid decl, output error message and
>> then exit.
>
> I've moved that over just to the gate, eventually there should be a routine
> that will initialize the builtins if they aren't by the FE.
>
>> > 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.
>
> But wrongly, for one adding the same stmt to multiple basic blocks
> is going to crash terribly, but also you IMHO want to insert just
> on fallthru edge, if the routine throws or longjmps, the result isn't
> written.
>
> --- gcc/tsan.c.jj       2012-11-13 16:46:21.000000000 +0100
> +++ gcc/tsan.c  2012-11-13 17:22:56.054837754 +0100
> @@ -1,4 +1,4 @@
> -/* GCC instrumentation plugin for ThreadSanitizer.
> +/* GCC instrumentation plugin for ThreadSanitizer.
>     Copyright (C) 2011, 2012 Free Software Foundation, Inc.
>     Contributed by Dmitry Vyukov <dvyukov@google.com>
>
> @@ -44,36 +44,27 @@ along with GCC; see the file COPYING3.
>     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;
> +                    : BUILT_IN_TSAN_READ_1;
>    else if (size <= 3)
> -    fcode = is_write ? BUILT_IN_TSAN_WRITE_2
> -                     : BUILT_IN_TSAN_READ_2;
> +    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;
> +    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;
> +    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;
> +    fcode = is_write ? BUILT_IN_TSAN_WRITE_16
> +                    : BUILT_IN_TSAN_READ_16;
>
> -  return get_tsan_builtin_decl (fcode);
> +  return builtin_decl_implicit (fcode);
>  }
>
>  /* Check as to whether EXPR refers to a store to vptr.  */
> @@ -81,14 +72,14 @@ get_memory_access_decl (bool is_write, u
>  static tree
>  is_vptr_store (gimple stmt, tree expr, bool is_write)
>  {
> -  if (is_write == true
> +  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);
> +         && DECL_VIRTUAL_P (field))
> +       return gimple_assign_rhs1 (stmt);
>      }
>    return NULL;
>  }
> @@ -96,7 +87,7 @@ is_vptr_store (gimple stmt, tree expr, b
>  /* Checks as to whether EXPR refers to constant var/field/param.
>     Don't bother to instrument them.  */
>
> -static bool
> +static bool
>  is_load_of_const_p (tree expr, bool is_write)
>  {
>    if (is_write)
> @@ -108,7 +99,7 @@ is_load_of_const_p (tree expr, bool is_w
>        || TREE_CODE (expr) == FIELD_DECL)
>      {
>        if (TREE_READONLY (expr))
> -        return true;
> +       return true;
>      }
>    return false;
>  }
> @@ -116,18 +107,14 @@ is_load_of_const_p (tree expr, bool is_w
>  /* Instruments EXPR if needed. If any instrumentation is inserted,
>   * return true. */
>
> -static bool
> +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;
> +  basic_block bb;
>    HOST_WIDE_INT size;
>    gimple stmt, g;
> -  gimple_stmt_iterator start_gsi;
>    location_t loc;
>
>    base = get_base_address (expr);
> @@ -144,8 +131,8 @@ instrument_expr (gimple_stmt_iterator gs
>        (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)
> +         && !TREE_ADDRESSABLE (expr)
> +         && TREE_STATIC (expr) == 0)
>        /* Not implemented.  */
>        || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE
>        /* Not implemented.  */
> @@ -156,22 +143,21 @@ instrument_expr (gimple_stmt_iterator gs
>        || 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;
> -            }
> -        }
> -    }
> +  size = int_size_in_bytes (TREE_TYPE (expr));
> +  if (size == -1)
> +    return false;
> +
> +  /* For now just avoid instrumenting bit field acceses.
> +     TODO: handle bit-fields as if touching the whole field.  */
> +  HOST_WIDE_INT bitsize, bitpos;
> +  tree offset;
> +  enum machine_mode mode;
> +  int volatilep = 0, unsignedp = 0;
> +  get_inner_reference (expr, &bitsize, &bitpos, &offset,
> +                      &mode, &unsignedp, &volatilep, false);
> +  if (bitpos % (size * BITS_PER_UNIT)
> +      || bitsize != size * BITS_PER_UNIT)
> +    return false;
>
>    /* TODO: handle other cases
>       (FIELD_DECL, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */
> @@ -186,44 +172,42 @@ instrument_expr (gimple_stmt_iterator gs
>    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);
> +  expr_ptr = build_fold_addr_expr (unshare_expr (expr));
>    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);
> +       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);
> +                            1, expr_ptr);
>      }
>    else
>      {
> -      builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_VPTR_UPDATE);
> +      builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_VPTR_UPDATE);
>        g = gimple_build_call (builtin_decl, 1, expr_ptr);
>      }
> -  gimple_set_location (g, loc);
> +  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 (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);
> -            }
> -        }
> +        a basic block, so the instrumented stmts need to be
> +        inserted in successor bbs. */
> +      if (is_ctrl_altering_stmt (stmt))
> +       {
> +         edge e;
> +
> +         bb = gsi_bb (gsi);
> +         e = find_fallthru_edge (bb->succs);
> +         if (e)
> +           gsi_insert_seq_on_edge_immediate (e, g);
> +       }
>        else
> -        gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +       gsi_insert_after (&gsi, g, GSI_NEW_STMT);
>      }
>    else
>      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> @@ -242,22 +226,22 @@ instrument_gimple (gimple_stmt_iterator
>    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;
> +  if (is_gimple_call (stmt)
> +      && (gimple_call_fndecl (stmt)
> +         != builtin_decl_implicit (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);
> -        }
> +       {
> +         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);
> -        }
> +       {
> +         rhs = gimple_assign_rhs1 (stmt);
> +         instrumented = instrument_expr (gsi, rhs, false);
> +       }
>      }
>    return instrumented;
>  }
> @@ -265,7 +249,7 @@ instrument_gimple (gimple_stmt_iterator
>  /* Instruments all interesting memory accesses in the current function.
>   * Return true if func entry/exit should be instrumented. */
>
> -static bool
> +static bool
>  instrument_memory_accesses (void)
>  {
>    basic_block bb;
> @@ -291,15 +275,15 @@ instrument_func_entry (void)
>    succ_bb = single_succ (ENTRY_BLOCK_PTR);
>    gsi = gsi_after_labels (succ_bb);
>
> -  builtin_decl = get_tsan_builtin_decl (BUILT_IN_RETURN_ADDRESS);
> +  builtin_decl = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
>    g = gimple_build_call (builtin_decl, 1, integer_zero_node);
> -  ret_addr = make_ssa_name (ptr_type_node, NULL);
> +  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);
> +  builtin_decl =  builtin_decl_implicit (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);
>  }
> @@ -325,7 +309,7 @@ instrument_func_exit (void)
>        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);
> +      builtin_decl = builtin_decl_implicit (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);
> @@ -350,70 +334,71 @@ tsan_pass (void)
>  static bool
>  tsan_gate (void)
>  {
> -  return flag_tsan != 0;
> +  return flag_tsan != 0 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
>  }
>
>  /* Inserts __tsan_init () into the list of CTORs.  */
>
> -void
> +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);
> +  init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
>    append_to_statement_list (build_call_expr (init_decl, 0),
> -                            &ctor_statements);
> +                           &ctor_statements);
>    cgraph_build_static_cdtor ('I', ctor_statements,
> -                             MAX_RESERVED_INIT_PRIORITY - 1);
> +                            MAX_RESERVED_INIT_PRIORITY - 1);
>  }
>
>  /* The pass descriptor.  */
>
> -struct gimple_opt_pass pass_tsan =
> +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  */
> +  "tsan",                              /* name  */
> +  OPTGROUP_NONE,                       /* optinfo_flags */
> +  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  */
> +  | TODO_update_address_taken          /* todo_flags_finish  */
>   }
>  };
>
> -static bool
> +static bool
>  tsan_gate_O0 (void)
> -{
> -  return flag_tsan != 0 && !optimize;
> -}
> +{
> +  return flag_tsan != 0 && !optimize;
> +}
>
> -struct gimple_opt_pass pass_tsan_O0 =
> +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  */
> +  "tsan0",                             /* name  */
> +  OPTGROUP_NONE,                       /* optinfo_flags */
> +  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  */
> +  | TODO_update_address_taken          /* todo_flags_finish  */
>   }
>  };
> -
>
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-13 17:25                 ` Xinliang David Li
@ 2012-11-13 17:36                   ` Jakub Jelinek
  2012-11-13 17:55                     ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-13 17:36 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Tue, Nov 13, 2012 at 09:25:36AM -0800, Xinliang David Li wrote:
> > That is complete misunderstanding of what update_address_taken does.
> > It removes TREE_ADDRESSABLE from addressables that are no longer
> > addressable, rather than adding TREE_ADDRESSABLE bits.
> 
> It will do the latter too. See iv-opts.

Where?  I strongly doubt that.

> >  For the latter
> > there is mark_addressable function.
> 
> This is certainly cheaper to use.

Even cheaper is just do nothing, tree-ssa-operands.c during
update_stmt_operands (which will be called for all newly created stmts,
upon update_stmt etc.) will take care of calling mark_address_taken.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-13 17:36                   ` Jakub Jelinek
@ 2012-11-13 17:55                     ` Xinliang David Li
  0 siblings, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-13 17:55 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Tue, Nov 13, 2012 at 9:36 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Nov 13, 2012 at 09:25:36AM -0800, Xinliang David Li wrote:
>> > That is complete misunderstanding of what update_address_taken does.
>> > It removes TREE_ADDRESSABLE from addressables that are no longer
>> > addressable, rather than adding TREE_ADDRESSABLE bits.
>>
>> It will do the latter too. See iv-opts.
>
> Where?  I strongly doubt that.

You are right. iv-opts is probably a good example that the update_stmt
can do the right job.

thanks,

David


>
>> >  For the latter
>> > there is mark_addressable function.
>>
>> This is certainly cheaper to use.
>
> Even cheaper is just do nothing, tree-ssa-operands.c during
> update_stmt_operands (which will be called for all newly created stmts,
> upon update_stmt etc.) will take care of calling mark_address_taken.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-13 16:41               ` Jakub Jelinek
  2012-11-13 17:25                 ` Xinliang David Li
@ 2012-11-14  0:08                 ` Wei Mi
  2012-11-14  0:54                   ` Richard Henderson
  1 sibling, 1 reply; 56+ messages in thread
From: Wei Mi @ 2012-11-14  0:08 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: GCC Patches, David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 19426 bytes --]

I updated the patch against trunk. At the same time, change the option
-faddress-sanitizer and -fthread-sanitizer to
-fsanitize=[address|thread].

Thanks,
Wei.

2012-11-13  Dmitry Vyukov  <dvyukov@google.com>
            Wei Mi  <wmi@google.com>

        * builtins.def (DEF_SANITIZER_BUILTIN): Define sanitize builtins.
        * sanitizer.def: Ditto.
        * Makefile.in (tsan.o): New.
        (BUILTINS_DEF): Add sanitizer.def.
        * passes.c (init_optimization_passes): Add tsan passes.
        * tree-pass.h (register_pass_info): Ditto.
        * toplev.c (compile_file): Ditto.
        * doc/invoke.texi: Document sanitize related options.
        * gcc.c (LINK_COMMAND_SPEC): Add -ltsan in link command if there
        -fsanitize=thread is on.
        * tsan.c: New file about tsan.
        * tsan.h: Ditto.
        * common.opt: Change -faddress-sanitizer and -fthread-sanitizer
        to -fsanitize=[address|thread].
        * flag-types.h (sanitize_type): Add enum sanitize_type.
        * cfgexpand.c (partition_stack_vars): Change flag_asan to
        flag_sanitizer.
        (expand_stack_vars): Ditto.
        * asan.c: Ditto.
        * varasm.c (assemble_noswitch_variable): Ditto
        (assemble_variable): Ditto


On Tue, Nov 13, 2012 at 8:40 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Mon, Nov 05, 2012 at 04:37:41PM -0800, Wei Mi wrote:
>> Thanks for the comments. I fix most of them except the setting of
>> TODO_.... The new patch.txt is attached.
>
> Please update the patch against trunk, it doesn't apply cleanly because
> of the asan commit.  I took the liberty to do at least some formatting
> cleanups and other small tweaks against your patch to tsan.c.
>
>> >> +  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.
>
> If it were just for the instrumentations, then you can easily update
> the vdef/vuse yourself, e.g. when inserting before a memory write,
> you can copy over the gimple_vuse of that write to gimple_vuse of the
> instrumentation call, create a new SSA_NAME for the gimple_vdef and
> and set the write's gimple_vuse to that new SSA_NAME.  For call
> instrumentation it would be a tiny bit harder, but for the instrumentation
> of function entry/exit you'd need to find out the current vop at that point.
> So perhaps we can live with that, at least for now.
>
>> >> +    | 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?
>
> That is complete misunderstanding of what update_address_taken does.
> It removes TREE_ADDRESSABLE from addressables that are no longer
> addressable, rather than adding TREE_ADDRESSABLE bits.  For the latter
> there is mark_addressable function.
>>
>> Wrap builtin_decl_implicit in get_tsan_builtin_decl. If
>> builtin_decl_implicit return invalid decl, output error message and
>> then exit.
>
> I've moved that over just to the gate, eventually there should be a routine
> that will initialize the builtins if they aren't by the FE.
>
>> > 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.
>
> But wrongly, for one adding the same stmt to multiple basic blocks
> is going to crash terribly, but also you IMHO want to insert just
> on fallthru edge, if the routine throws or longjmps, the result isn't
> written.
>
> --- gcc/tsan.c.jj       2012-11-13 16:46:21.000000000 +0100
> +++ gcc/tsan.c  2012-11-13 17:22:56.054837754 +0100
> @@ -1,4 +1,4 @@
> -/* GCC instrumentation plugin for ThreadSanitizer.
> +/* GCC instrumentation plugin for ThreadSanitizer.
>     Copyright (C) 2011, 2012 Free Software Foundation, Inc.
>     Contributed by Dmitry Vyukov <dvyukov@google.com>
>
> @@ -44,36 +44,27 @@ along with GCC; see the file COPYING3.
>     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;
> +                    : BUILT_IN_TSAN_READ_1;
>    else if (size <= 3)
> -    fcode = is_write ? BUILT_IN_TSAN_WRITE_2
> -                     : BUILT_IN_TSAN_READ_2;
> +    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;
> +    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;
> +    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;
> +    fcode = is_write ? BUILT_IN_TSAN_WRITE_16
> +                    : BUILT_IN_TSAN_READ_16;
>
> -  return get_tsan_builtin_decl (fcode);
> +  return builtin_decl_implicit (fcode);
>  }
>
>  /* Check as to whether EXPR refers to a store to vptr.  */
> @@ -81,14 +72,14 @@ get_memory_access_decl (bool is_write, u
>  static tree
>  is_vptr_store (gimple stmt, tree expr, bool is_write)
>  {
> -  if (is_write == true
> +  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);
> +         && DECL_VIRTUAL_P (field))
> +       return gimple_assign_rhs1 (stmt);
>      }
>    return NULL;
>  }
> @@ -96,7 +87,7 @@ is_vptr_store (gimple stmt, tree expr, b
>  /* Checks as to whether EXPR refers to constant var/field/param.
>     Don't bother to instrument them.  */
>
> -static bool
> +static bool
>  is_load_of_const_p (tree expr, bool is_write)
>  {
>    if (is_write)
> @@ -108,7 +99,7 @@ is_load_of_const_p (tree expr, bool is_w
>        || TREE_CODE (expr) == FIELD_DECL)
>      {
>        if (TREE_READONLY (expr))
> -        return true;
> +       return true;
>      }
>    return false;
>  }
> @@ -116,18 +107,14 @@ is_load_of_const_p (tree expr, bool is_w
>  /* Instruments EXPR if needed. If any instrumentation is inserted,
>   * return true. */
>
> -static bool
> +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;
> +  basic_block bb;
>    HOST_WIDE_INT size;
>    gimple stmt, g;
> -  gimple_stmt_iterator start_gsi;
>    location_t loc;
>
>    base = get_base_address (expr);
> @@ -144,8 +131,8 @@ instrument_expr (gimple_stmt_iterator gs
>        (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)
> +         && !TREE_ADDRESSABLE (expr)
> +         && TREE_STATIC (expr) == 0)
>        /* Not implemented.  */
>        || TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE
>        /* Not implemented.  */
> @@ -156,22 +143,21 @@ instrument_expr (gimple_stmt_iterator gs
>        || 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;
> -            }
> -        }
> -    }
> +  size = int_size_in_bytes (TREE_TYPE (expr));
> +  if (size == -1)
> +    return false;
> +
> +  /* For now just avoid instrumenting bit field acceses.
> +     TODO: handle bit-fields as if touching the whole field.  */
> +  HOST_WIDE_INT bitsize, bitpos;
> +  tree offset;
> +  enum machine_mode mode;
> +  int volatilep = 0, unsignedp = 0;
> +  get_inner_reference (expr, &bitsize, &bitpos, &offset,
> +                      &mode, &unsignedp, &volatilep, false);
> +  if (bitpos % (size * BITS_PER_UNIT)
> +      || bitsize != size * BITS_PER_UNIT)
> +    return false;
>
>    /* TODO: handle other cases
>       (FIELD_DECL, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */
> @@ -186,44 +172,42 @@ instrument_expr (gimple_stmt_iterator gs
>    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);
> +  expr_ptr = build_fold_addr_expr (unshare_expr (expr));
>    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);
> +       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);
> +                            1, expr_ptr);
>      }
>    else
>      {
> -      builtin_decl = get_tsan_builtin_decl (BUILT_IN_TSAN_VPTR_UPDATE);
> +      builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_VPTR_UPDATE);
>        g = gimple_build_call (builtin_decl, 1, expr_ptr);
>      }
> -  gimple_set_location (g, loc);
> +  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 (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);
> -            }
> -        }
> +        a basic block, so the instrumented stmts need to be
> +        inserted in successor bbs. */
> +      if (is_ctrl_altering_stmt (stmt))
> +       {
> +         edge e;
> +
> +         bb = gsi_bb (gsi);
> +         e = find_fallthru_edge (bb->succs);
> +         if (e)
> +           gsi_insert_seq_on_edge_immediate (e, g);
> +       }
>        else
> -        gsi_insert_after (&gsi, g, GSI_NEW_STMT);
> +       gsi_insert_after (&gsi, g, GSI_NEW_STMT);
>      }
>    else
>      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
> @@ -242,22 +226,22 @@ instrument_gimple (gimple_stmt_iterator
>    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;
> +  if (is_gimple_call (stmt)
> +      && (gimple_call_fndecl (stmt)
> +         != builtin_decl_implicit (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);
> -        }
> +       {
> +         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);
> -        }
> +       {
> +         rhs = gimple_assign_rhs1 (stmt);
> +         instrumented = instrument_expr (gsi, rhs, false);
> +       }
>      }
>    return instrumented;
>  }
> @@ -265,7 +249,7 @@ instrument_gimple (gimple_stmt_iterator
>  /* Instruments all interesting memory accesses in the current function.
>   * Return true if func entry/exit should be instrumented. */
>
> -static bool
> +static bool
>  instrument_memory_accesses (void)
>  {
>    basic_block bb;
> @@ -291,15 +275,15 @@ instrument_func_entry (void)
>    succ_bb = single_succ (ENTRY_BLOCK_PTR);
>    gsi = gsi_after_labels (succ_bb);
>
> -  builtin_decl = get_tsan_builtin_decl (BUILT_IN_RETURN_ADDRESS);
> +  builtin_decl = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
>    g = gimple_build_call (builtin_decl, 1, integer_zero_node);
> -  ret_addr = make_ssa_name (ptr_type_node, NULL);
> +  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);
> +  builtin_decl =  builtin_decl_implicit (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);
>  }
> @@ -325,7 +309,7 @@ instrument_func_exit (void)
>        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);
> +      builtin_decl = builtin_decl_implicit (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);
> @@ -350,70 +334,71 @@ tsan_pass (void)
>  static bool
>  tsan_gate (void)
>  {
> -  return flag_tsan != 0;
> +  return flag_tsan != 0 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
>  }
>
>  /* Inserts __tsan_init () into the list of CTORs.  */
>
> -void
> +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);
> +  init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
>    append_to_statement_list (build_call_expr (init_decl, 0),
> -                            &ctor_statements);
> +                           &ctor_statements);
>    cgraph_build_static_cdtor ('I', ctor_statements,
> -                             MAX_RESERVED_INIT_PRIORITY - 1);
> +                            MAX_RESERVED_INIT_PRIORITY - 1);
>  }
>
>  /* The pass descriptor.  */
>
> -struct gimple_opt_pass pass_tsan =
> +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  */
> +  "tsan",                              /* name  */
> +  OPTGROUP_NONE,                       /* optinfo_flags */
> +  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  */
> +  | TODO_update_address_taken          /* todo_flags_finish  */
>   }
>  };
>
> -static bool
> +static bool
>  tsan_gate_O0 (void)
> -{
> -  return flag_tsan != 0 && !optimize;
> -}
> +{
> +  return flag_tsan != 0 && !optimize;
> +}
>
> -struct gimple_opt_pass pass_tsan_O0 =
> +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  */
> +  "tsan0",                             /* name  */
> +  OPTGROUP_NONE,                       /* optinfo_flags */
> +  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  */
> +  | TODO_update_address_taken          /* todo_flags_finish  */
>   }
>  };
> -
>
>
>         Jakub

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 30535 bytes --]

Index: gcc/gcc.c
===================================================================
--- gcc/gcc.c	(revision 193482)
+++ gcc/gcc.c	(working copy)
@@ -687,7 +687,8 @@ proper position among the other output f
     %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\
     %(mflib) " STACK_SPLIT_SPEC "\
     %{fprofile-arcs|fprofile-generate*|coverage:-lgcov}\
-    %{faddress-sanitizer:-lasan}\
+    %{fsanitize=address:-lasan}\
+    %{fsanitize=thread:-ltsan}\
     %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\
     %{!nostdlib:%{!nostartfiles:%E}} %{T*} }}}}}}"
 #endif
Index: gcc/tree-pass.h
===================================================================
--- gcc/tree-pass.h	(revision 193482)
+++ gcc/tree-pass.h	(working copy)
@@ -261,6 +261,8 @@ extern struct gimple_opt_pass pass_mudfl
 extern struct gimple_opt_pass pass_mudflap_2;
 extern struct gimple_opt_pass pass_asan;
 extern struct gimple_opt_pass pass_asan_O0;
+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/varasm.c
===================================================================
--- gcc/varasm.c	(revision 193482)
+++ gcc/varasm.c	(working copy)
@@ -1832,7 +1832,7 @@ assemble_noswitch_variable (tree decl, c
   size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
   rounded = size;
 
-  if (flag_asan && asan_protect_global (decl))
+  if (flag_sanitize == SANITIZE_ADDRESS && asan_protect_global (decl))
     size += asan_red_zone_size (size);
 
   /* Don't allocate zero bytes of common,
@@ -1990,7 +1990,7 @@ assemble_variable (tree decl, int top_le
 
   align_variable (decl, dont_output_data);
 
-  if (flag_asan
+  if (flag_sanitize == SANITIZE_ADDRESS
       && asan_protect_global (decl))
     {
       asan_protected = true;
@@ -6946,7 +6946,8 @@ place_block_symbol (rtx symbol)
       decl = SYMBOL_REF_DECL (symbol);
       alignment = DECL_ALIGN (decl);
       size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
-      if (flag_asan && asan_protect_global (decl))
+      if (flag_sanitize == SANITIZE_ADDRESS 
+	  && asan_protect_global (decl))
 	size += asan_red_zone_size (size);
     }
 
Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 193482)
+++ gcc/passes.c	(working copy)
@@ -1457,6 +1457,7 @@ init_optimization_passes (void)
       NEXT_PASS (pass_pre);
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_tree_loop);
 	{
 	  struct opt_pass **p = &pass_tree_loop.pass.sub;
@@ -1563,6 +1564,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_lower_complex_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_cleanup_eh);
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
Index: gcc/builtins.def
===================================================================
--- gcc/builtins.def	(revision 193482)
+++ gcc/builtins.def	(working copy)
@@ -149,6 +149,15 @@ 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_sanitize == SANITIZE_THREAD)
+
 /* 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 +834,7 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE",
 
 /* GTM builtins. */
 #include "gtm-builtins.def"
+
+/* Sanitizer builtins. */
+#include "sanitizer.def"
+
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 193490)
+++ gcc/doc/invoke.texi	(working copy)
@@ -289,7 +289,8 @@ Objective-C and Objective-C++ Dialects}.
 @item Debugging Options
 @xref{Debugging Options,,Options for Debugging Your Program or GCC}.
 @gccoptlist{-d@var{letters}  -dumpspecs  -dumpmachine  -dumpversion @gol
--faddress-sanitizer -fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
+-fsanitize=@var{style} @gol
+-fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
 -fdisable-ipa-@var{pass_name} @gol
 -fdisable-rtl-@var{pass_name} @gol
 -fdisable-rtl-@var{pass-name}=@var{range-list} @gol
@@ -309,6 +310,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
@@ -382,8 +384,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-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
@@ -5983,6 +5985,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
@@ -6849,11 +6856,14 @@ assumptions based on that.
 
 The default is @option{-fzero-initialized-in-bss}.
 
-@item -faddress-sanitizer
-Enable AddressSanitizer, a fast memory error detector.
-Memory access instructions will be instrumented to detect
-out-of-bounds and use-after-free bugs. So far only heap bugs will be detected.
-See @uref{http://code.google.com/p/address-sanitizer/} for more details.
+@item -fsanitize=[address|thread]
+Enable AddressSanitizer or ThreadSanitizer. AddressSanitizer is a fast 
+memory error detector. Memory access instructions will be instrumented 
+to detect out-of-bounds, use-after-free, stack overflow and global 
+overflow bugs. ThreadSanitizer is a fast data race detector.  
+See @uref{http://code.google.com/p/address-sanitizer/} and 
+@uref{http://code.google.com/p/data-race-test/wiki/ThreadSanitizer} 
+for more details.
 
 @item -fmudflap -fmudflapth -fmudflapir
 @opindex fmudflap
@@ -6881,6 +6891,11 @@ 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-ignore
+@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/tsan.c
===================================================================
--- gcc/tsan.c	(revision 0)
+++ gcc/tsan.c	(revision 0)
@@ -0,0 +1,406 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<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 "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_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 builtin_decl_implicit (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;
+  tree base, rhs, expr_type, expr_ptr, builtin_decl;
+  basic_block bb;
+  HOST_WIDE_INT size;
+  gimple stmt, g;
+  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;
+
+  size = int_size_in_bytes (TREE_TYPE (expr));
+  if (size == -1)
+    return false;
+
+  /* For now just avoid instrumenting bit field acceses.
+     TODO: handle bit-fields as if touching the whole field.  */
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  get_inner_reference (expr, &bitsize, &bitpos, &offset,
+		       &mode, &unsignedp, &volatilep, false);
+  if (bitpos % (size * BITS_PER_UNIT)
+      || bitsize != size * BITS_PER_UNIT)
+    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_fold_addr_expr (unshare_expr (expr));
+  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 = builtin_decl_implicit (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 basic block, so the instrumented stmts need to be
+	 inserted in successor bbs. */
+      if (is_ctrl_altering_stmt (stmt))
+	{
+	  edge e;
+
+	  bb = gsi_bb (gsi);
+	  e = find_fallthru_edge (bb->succs);
+	  if (e)
+	    gsi_insert_seq_on_edge_immediate (e, g);
+	}
+      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)
+	  != builtin_decl_implicit (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 = builtin_decl_implicit (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 =  builtin_decl_implicit (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 = builtin_decl_implicit (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_sanitize == SANITIZE_THREAD 
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+/* 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 = builtin_decl_implicit (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  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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_sanitize == SANITIZE_THREAD && !optimize
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+struct gimple_opt_pass pass_tsan_O0 =
+{
+ {
+  GIMPLE_PASS,
+  "tsan0",				/* name  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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/common.opt
===================================================================
--- gcc/common.opt	(revision 193482)
+++ gcc/common.opt	(working copy)
@@ -837,9 +837,18 @@ fargument-noalias-anything
 Common Ignore
 Does nothing. Preserved for backward compatibility.
 
-faddress-sanitizer
-Common Report Var(flag_asan)
-Enable AddressSanitizer, a memory error detector
+fsanitize=
+Common Joined RejectNegative Enum(sanitize_type) Var(flag_sanitize) Init(SANITIZE_OFF)
+-fsanitize=[address|thread]	Specify to run a type of sanitize test
+
+Enum
+Name(sanitize_type) Type(sanitize_type) UnknownError(unknown sanitize type %qs)
+
+EnumValue
+Enum(sanitize_type) String(address) Value(SANITIZE_ADDRESS)
+
+EnumValue
+Enum(sanitize_type) String(thread) Value(SANITIZE_THREAD)
 
 fasynchronous-unwind-tables
 Common Report Var(flag_asynchronous_unwind_tables) Optimization
@@ -1510,6 +1519,10 @@ fmove-loop-invariants
 Common Report Var(flag_move_loop_invariants) Init(1) Optimization
 Move loop invariant computations out of loops
 
+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/tsan.h
===================================================================
--- gcc/tsan.h	(revision 0)
+++ gcc/tsan.h	(revision 0)
@@ -0,0 +1,52 @@
+/* ThreadSanitizer, a data race detector.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef TREE_TSAN
+#define TREE_TSAN
+
+extern void tsan_finish_file (void);
+
+#endif /* TREE_TSAN */
+/* ThreadSanitizer, a data race detector.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef TREE_TSAN
+#define TREE_TSAN
+
+extern void tsan_finish_file (void);
+
+#endif /* TREE_TSAN */
Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 193482)
+++ gcc/asan.c	(working copy)
@@ -1587,7 +1587,7 @@ asan_instrument (void)
 static bool
 gate_asan (void)
 {
-  return flag_asan != 0;
+  return flag_sanitize == SANITIZE_ADDRESS;
 }
 
 struct gimple_opt_pass pass_asan =
@@ -1614,7 +1614,7 @@ struct gimple_opt_pass pass_asan =
 static bool
 gate_asan_O0 (void)
 {
-  return flag_asan != 0 && !optimize;
+  return flag_sanitize == SANITIZE_ADDRESS && !optimize;
 }
 
 struct gimple_opt_pass pass_asan_O0 =
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/cfgexpand.c
===================================================================
--- gcc/cfgexpand.c	(revision 193482)
+++ gcc/cfgexpand.c	(working copy)
@@ -765,7 +765,7 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (flag_asan && isize != jsize
+	  if (flag_sanitize == SANITIZE_ADDRESS && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -944,7 +944,7 @@ expand_stack_vars (bool (*pred) (size_t)
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  if (flag_asan && pred)
+	  if (flag_sanitize == SANITIZE_ADDRESS && pred)
 	    {
 	      HOST_WIDE_INT prev_offset = frame_offset;
 	      tree repr_decl = NULL_TREE;
@@ -1116,7 +1116,7 @@ defer_stack_allocation (tree var, bool t
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
      Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || flag_asan)
+  if (flag_stack_protect || flag_sanitize == SANITIZE_ADDRESS)
     return true;
 
   /* We handle "large" alignment via dynamic allocation.  We want to handle
@@ -1698,7 +1698,7 @@ expand_used_vars (void)
 	    expand_stack_vars (stack_protect_decl_phase_2, &data);
 	}
 
-      if (flag_asan)
+      if (flag_sanitize == SANITIZE_ADDRESS)
 	/* Phase 3, any partitions that need asan protection
 	   in addition to phase 1 and 2.  */
 	expand_stack_vars (asan_decl_phase_3, &data);
Index: gcc/toplev.c
===================================================================
--- gcc/toplev.c	(revision 193482)
+++ gcc/toplev.c	(working copy)
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3.
 #include "tree-mudflap.h"
 #include "asan.h"
 #include "gimple.h"
+#include "tsan.h"
 #include "tree-ssa-alias.h"
 #include "plugin.h"
 
@@ -572,9 +573,13 @@ compile_file (void)
 	mudflap_finish_file ();
 
       /* File-scope initialization for AddressSanitizer.  */
-      if (flag_asan)
+      if (flag_sanitize == SANITIZE_ADDRESS)
         asan_finish_file ();
 
+      /* File-scope initialization for ThreadSanitizer.  */
+      if (flag_sanitize == SANITIZE_THREAD)
+        tsan_finish_file ();
+
       output_shared_constant_pool ();
       output_object_blocks ();
       finish_tm_clone_pairs ();
@@ -1545,12 +1550,12 @@ process_options (void)
     }
 
   /* Address Sanitizer needs porting to each target architecture.  */
-  if (flag_asan
+  if (flag_sanitize == SANITIZE_ADDRESS
       && (targetm.asan_shadow_offset == NULL
 	  || !FRAME_GROWS_DOWNWARD))
     {
-      warning (0, "-faddress-sanitizer not supported for this target");
-      flag_asan = 0;
+      warning (0, "-fsanitize=address not supported for this target");
+      flag_sanitize = SANITIZE_OFF;
     }
 
   /* Enable -Werror=coverage-mismatch when -Werror and -Wno-error
Index: gcc/flag-types.h
===================================================================
--- gcc/flag-types.h	(revision 193482)
+++ gcc/flag-types.h	(working copy)
@@ -200,4 +200,11 @@ enum fp_contract_mode {
   FP_CONTRACT_FAST = 2
 };
 
+/* Sanitize mode */
+enum sanitize_type {
+  SANITIZE_OFF,
+  SANITIZE_ADDRESS,
+  SANITIZE_THREAD
+};
+
 #endif /* ! GCC_FLAG_TYPES_H */
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 193482)
+++ 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 \
@@ -1350,6 +1350,7 @@ OBJS = \
 	trans-mem.o \
 	tree-affine.o \
 	asan.o \
+	tsan.o \
 	tree-call-cdce.o \
 	tree-cfg.o \
 	tree-cfgcleanup.o \
@@ -2212,6 +2213,12 @@ asan.o : asan.c asan.h $(CONFIG_H) $(SYS
    output.h coretypes.h $(GIMPLE_PRETTY_PRINT_H) \
    tree-iterator.h $(TREE_FLOW_H) $(TREE_PASS_H) \
    $(TARGET_H) $(EXPR_H) $(OPTABS_H) $(TM_P_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-ssa-tail-merge.o: tree-ssa-tail-merge.c \
    $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \
    $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \
@@ -2674,7 +2681,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)
 
@@ -3726,6 +3734,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp
   $(srcdir)/target-globals.h \
   $(srcdir)/ipa-inline.h \
   $(srcdir)/asan.c \
+  $(srcdir)/tsan.c \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-14  0:08                 ` Wei Mi
@ 2012-11-14  0:54                   ` Richard Henderson
  2012-11-14  1:06                     ` Wei Mi
  0 siblings, 1 reply; 56+ messages in thread
From: Richard Henderson @ 2012-11-14  0:54 UTC (permalink / raw)
  To: Wei Mi
  Cc: Jakub Jelinek, GCC Patches, David Li, Diego Novillo,
	Dodji Seketeli, Dmitry Vyukov

On 11/13/2012 04:08 PM, Wei Mi wrote:
> +extern void tsan_finish_file (void);
> +
> +#endif /* TREE_TSAN */
> +/* ThreadSanitizer, a data race detector.
> +   Copyright (C) 2011 Free Software Foundation, Inc.
> +   Contributed by Dmitry Vyukov <dvyukov@google.com>

Careful, you've got double applied patches there.


r~

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-14  0:54                   ` Richard Henderson
@ 2012-11-14  1:06                     ` Wei Mi
  2012-11-16 23:13                       ` Wei Mi
  0 siblings, 1 reply; 56+ messages in thread
From: Wei Mi @ 2012-11-14  1:06 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Jakub Jelinek, GCC Patches, David Li, Diego Novillo,
	Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 473 bytes --]

Thanks for catching this. I update the patch.

Regards,
Wei.

On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson <rth@redhat.com> wrote:
> On 11/13/2012 04:08 PM, Wei Mi wrote:
>> +extern void tsan_finish_file (void);
>> +
>> +#endif /* TREE_TSAN */
>> +/* ThreadSanitizer, a data race detector.
>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>
> Careful, you've got double applied patches there.
>
>
> r~

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 29644 bytes --]

Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 193482)
+++ gcc/passes.c	(working copy)
@@ -1457,6 +1457,7 @@ init_optimization_passes (void)
       NEXT_PASS (pass_pre);
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_tree_loop);
 	{
 	  struct opt_pass **p = &pass_tree_loop.pass.sub;
@@ -1563,6 +1564,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_lower_complex_O0);
   NEXT_PASS (pass_asan_O0);
+  NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_cleanup_eh);
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 193490)
+++ gcc/doc/invoke.texi	(working copy)
@@ -289,7 +289,8 @@ Objective-C and Objective-C++ Dialects}.
 @item Debugging Options
 @xref{Debugging Options,,Options for Debugging Your Program or GCC}.
 @gccoptlist{-d@var{letters}  -dumpspecs  -dumpmachine  -dumpversion @gol
--faddress-sanitizer -fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
+-fsanitize=@var{style} @gol
+-fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
 -fdisable-ipa-@var{pass_name} @gol
 -fdisable-rtl-@var{pass_name} @gol
 -fdisable-rtl-@var{pass-name}=@var{range-list} @gol
@@ -309,6 +310,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
@@ -382,8 +384,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-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
@@ -5983,6 +5985,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
@@ -6849,11 +6856,14 @@ assumptions based on that.
 
 The default is @option{-fzero-initialized-in-bss}.
 
-@item -faddress-sanitizer
-Enable AddressSanitizer, a fast memory error detector.
-Memory access instructions will be instrumented to detect
-out-of-bounds and use-after-free bugs. So far only heap bugs will be detected.
-See @uref{http://code.google.com/p/address-sanitizer/} for more details.
+@item -fsanitize=[address|thread]
+Enable AddressSanitizer or ThreadSanitizer. AddressSanitizer is a fast 
+memory error detector. Memory access instructions will be instrumented 
+to detect out-of-bounds, use-after-free, stack overflow and global 
+overflow bugs. ThreadSanitizer is a fast data race detector.  
+See @uref{http://code.google.com/p/address-sanitizer/} and 
+@uref{http://code.google.com/p/data-race-test/wiki/ThreadSanitizer} 
+for more details.
 
 @item -fmudflap -fmudflapth -fmudflapir
 @opindex fmudflap
@@ -6881,6 +6891,11 @@ 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-ignore
+@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/tsan.c
===================================================================
--- gcc/tsan.c	(revision 0)
+++ gcc/tsan.c	(revision 0)
@@ -0,0 +1,406 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<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 "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_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 builtin_decl_implicit (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;
+  tree base, rhs, expr_type, expr_ptr, builtin_decl;
+  basic_block bb;
+  HOST_WIDE_INT size;
+  gimple stmt, g;
+  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;
+
+  size = int_size_in_bytes (TREE_TYPE (expr));
+  if (size == -1)
+    return false;
+
+  /* For now just avoid instrumenting bit field acceses.
+     TODO: handle bit-fields as if touching the whole field.  */
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  get_inner_reference (expr, &bitsize, &bitpos, &offset,
+		       &mode, &unsignedp, &volatilep, false);
+  if (bitpos % (size * BITS_PER_UNIT)
+      || bitsize != size * BITS_PER_UNIT)
+    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_fold_addr_expr (unshare_expr (expr));
+  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 = builtin_decl_implicit (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 basic block, so the instrumented stmts need to be
+	 inserted in successor bbs. */
+      if (is_ctrl_altering_stmt (stmt))
+	{
+	  edge e;
+
+	  bb = gsi_bb (gsi);
+	  e = find_fallthru_edge (bb->succs);
+	  if (e)
+	    gsi_insert_seq_on_edge_immediate (e, g);
+	}
+      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)
+	  != builtin_decl_implicit (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 = builtin_decl_implicit (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 =  builtin_decl_implicit (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 = builtin_decl_implicit (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_sanitize == SANITIZE_THREAD 
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+/* 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 = builtin_decl_implicit (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  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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_sanitize == SANITIZE_THREAD && !optimize
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+struct gimple_opt_pass pass_tsan_O0 =
+{
+ {
+  GIMPLE_PASS,
+  "tsan0",				/* name  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef TREE_TSAN
+#define TREE_TSAN
+
+extern void tsan_finish_file (void);
+
+#endif /* TREE_TSAN */
Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 193482)
+++ gcc/asan.c	(working copy)
@@ -1587,7 +1587,7 @@ asan_instrument (void)
 static bool
 gate_asan (void)
 {
-  return flag_asan != 0;
+  return flag_sanitize == SANITIZE_ADDRESS;
 }
 
 struct gimple_opt_pass pass_asan =
@@ -1614,7 +1614,7 @@ struct gimple_opt_pass pass_asan =
 static bool
 gate_asan_O0 (void)
 {
-  return flag_asan != 0 && !optimize;
+  return flag_sanitize == SANITIZE_ADDRESS && !optimize;
 }
 
 struct gimple_opt_pass pass_asan_O0 =
Index: gcc/common.opt
===================================================================
--- gcc/common.opt	(revision 193482)
+++ gcc/common.opt	(working copy)
@@ -837,9 +837,18 @@ fargument-noalias-anything
 Common Ignore
 Does nothing. Preserved for backward compatibility.
 
-faddress-sanitizer
-Common Report Var(flag_asan)
-Enable AddressSanitizer, a memory error detector
+fsanitize=
+Common Joined RejectNegative Enum(sanitize_type) Var(flag_sanitize) Init(SANITIZE_OFF)
+-fsanitize=[address|thread]	Specify to run a type of sanitize test
+
+Enum
+Name(sanitize_type) Type(sanitize_type) UnknownError(unknown sanitize type %qs)
+
+EnumValue
+Enum(sanitize_type) String(address) Value(SANITIZE_ADDRESS)
+
+EnumValue
+Enum(sanitize_type) String(thread) Value(SANITIZE_THREAD)
 
 fasynchronous-unwind-tables
 Common Report Var(flag_asynchronous_unwind_tables) Optimization
@@ -1510,6 +1519,10 @@ fmove-loop-invariants
 Common Report Var(flag_move_loop_invariants) Init(1) Optimization
 Move loop invariant computations out of loops
 
+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/builtins.def
===================================================================
--- gcc/builtins.def	(revision 193482)
+++ gcc/builtins.def	(working copy)
@@ -149,6 +149,15 @@ 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_sanitize == SANITIZE_THREAD)
+
 /* 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 +834,7 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE",
 
 /* GTM builtins. */
 #include "gtm-builtins.def"
+
+/* Sanitizer builtins. */
+#include "sanitizer.def"
+
Index: gcc/toplev.c
===================================================================
--- gcc/toplev.c	(revision 193482)
+++ gcc/toplev.c	(working copy)
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3.
 #include "tree-mudflap.h"
 #include "asan.h"
 #include "gimple.h"
+#include "tsan.h"
 #include "tree-ssa-alias.h"
 #include "plugin.h"
 
@@ -572,9 +573,13 @@ compile_file (void)
 	mudflap_finish_file ();
 
       /* File-scope initialization for AddressSanitizer.  */
-      if (flag_asan)
+      if (flag_sanitize == SANITIZE_ADDRESS)
         asan_finish_file ();
 
+      /* File-scope initialization for ThreadSanitizer.  */
+      if (flag_sanitize == SANITIZE_THREAD)
+        tsan_finish_file ();
+
       output_shared_constant_pool ();
       output_object_blocks ();
       finish_tm_clone_pairs ();
@@ -1545,12 +1550,12 @@ process_options (void)
     }
 
   /* Address Sanitizer needs porting to each target architecture.  */
-  if (flag_asan
+  if (flag_sanitize == SANITIZE_ADDRESS
       && (targetm.asan_shadow_offset == NULL
 	  || !FRAME_GROWS_DOWNWARD))
     {
-      warning (0, "-faddress-sanitizer not supported for this target");
-      flag_asan = 0;
+      warning (0, "-fsanitize=address not supported for this target");
+      flag_sanitize = SANITIZE_OFF;
     }
 
   /* Enable -Werror=coverage-mismatch when -Werror and -Wno-error
Index: gcc/cfgexpand.c
===================================================================
--- gcc/cfgexpand.c	(revision 193482)
+++ gcc/cfgexpand.c	(working copy)
@@ -765,7 +765,7 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (flag_asan && isize != jsize
+	  if (flag_sanitize == SANITIZE_ADDRESS && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -944,7 +944,7 @@ expand_stack_vars (bool (*pred) (size_t)
       alignb = stack_vars[i].alignb;
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
-	  if (flag_asan && pred)
+	  if (flag_sanitize == SANITIZE_ADDRESS && pred)
 	    {
 	      HOST_WIDE_INT prev_offset = frame_offset;
 	      tree repr_decl = NULL_TREE;
@@ -1116,7 +1116,7 @@ defer_stack_allocation (tree var, bool t
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
      Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || flag_asan)
+  if (flag_stack_protect || flag_sanitize == SANITIZE_ADDRESS)
     return true;
 
   /* We handle "large" alignment via dynamic allocation.  We want to handle
@@ -1698,7 +1698,7 @@ expand_used_vars (void)
 	    expand_stack_vars (stack_protect_decl_phase_2, &data);
 	}
 
-      if (flag_asan)
+      if (flag_sanitize == SANITIZE_ADDRESS)
 	/* Phase 3, any partitions that need asan protection
 	   in addition to phase 1 and 2.  */
 	expand_stack_vars (asan_decl_phase_3, &data);
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/flag-types.h
===================================================================
--- gcc/flag-types.h	(revision 193482)
+++ gcc/flag-types.h	(working copy)
@@ -200,4 +200,11 @@ enum fp_contract_mode {
   FP_CONTRACT_FAST = 2
 };
 
+/* Sanitize mode */
+enum sanitize_type {
+  SANITIZE_OFF,
+  SANITIZE_ADDRESS,
+  SANITIZE_THREAD
+};
+
 #endif /* ! GCC_FLAG_TYPES_H */
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 193482)
+++ 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 \
@@ -1350,6 +1350,7 @@ OBJS = \
 	trans-mem.o \
 	tree-affine.o \
 	asan.o \
+	tsan.o \
 	tree-call-cdce.o \
 	tree-cfg.o \
 	tree-cfgcleanup.o \
@@ -2212,6 +2213,12 @@ asan.o : asan.c asan.h $(CONFIG_H) $(SYS
    output.h coretypes.h $(GIMPLE_PRETTY_PRINT_H) \
    tree-iterator.h $(TREE_FLOW_H) $(TREE_PASS_H) \
    $(TARGET_H) $(EXPR_H) $(OPTABS_H) $(TM_P_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-ssa-tail-merge.o: tree-ssa-tail-merge.c \
    $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \
    $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \
@@ -2674,7 +2681,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)
 
@@ -3726,6 +3734,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp
   $(srcdir)/target-globals.h \
   $(srcdir)/ipa-inline.h \
   $(srcdir)/asan.c \
+  $(srcdir)/tsan.c \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
Index: gcc/gcc.c
===================================================================
--- gcc/gcc.c	(revision 193482)
+++ gcc/gcc.c	(working copy)
@@ -687,7 +687,8 @@ proper position among the other output f
     %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\
     %(mflib) " STACK_SPLIT_SPEC "\
     %{fprofile-arcs|fprofile-generate*|coverage:-lgcov}\
-    %{faddress-sanitizer:-lasan}\
+    %{fsanitize=address:-lasan}\
+    %{fsanitize=thread:-ltsan}\
     %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\
     %{!nostdlib:%{!nostartfiles:%E}} %{T*} }}}}}}"
 #endif
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c	(revision 193482)
+++ gcc/varasm.c	(working copy)
@@ -1832,7 +1832,7 @@ assemble_noswitch_variable (tree decl, c
   size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
   rounded = size;
 
-  if (flag_asan && asan_protect_global (decl))
+  if (flag_sanitize == SANITIZE_ADDRESS && asan_protect_global (decl))
     size += asan_red_zone_size (size);
 
   /* Don't allocate zero bytes of common,
@@ -1990,7 +1990,7 @@ assemble_variable (tree decl, int top_le
 
   align_variable (decl, dont_output_data);
 
-  if (flag_asan
+  if (flag_sanitize == SANITIZE_ADDRESS
       && asan_protect_global (decl))
     {
       asan_protected = true;
@@ -6946,7 +6946,8 @@ place_block_symbol (rtx symbol)
       decl = SYMBOL_REF_DECL (symbol);
       alignment = DECL_ALIGN (decl);
       size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
-      if (flag_asan && asan_protect_global (decl))
+      if (flag_sanitize == SANITIZE_ADDRESS 
+	  && asan_protect_global (decl))
 	size += asan_red_zone_size (size);
     }
 
Index: gcc/tree-pass.h
===================================================================
--- gcc/tree-pass.h	(revision 193482)
+++ gcc/tree-pass.h	(working copy)
@@ -261,6 +261,8 @@ extern struct gimple_opt_pass pass_mudfl
 extern struct gimple_opt_pass pass_mudflap_2;
 extern struct gimple_opt_pass pass_asan;
 extern struct gimple_opt_pass pass_asan_O0;
+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;

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-14  1:06                     ` Wei Mi
@ 2012-11-16 23:13                       ` Wei Mi
  2012-11-18 18:52                         ` Konstantin Serebryany
  0 siblings, 1 reply; 56+ messages in thread
From: Wei Mi @ 2012-11-16 23:13 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Jakub Jelinek, GCC Patches, David Li, Diego Novillo,
	Dodji Seketeli, Dmitry Vyukov

Hi,

Is it ok for the trunk?

Thanks,
Wei.

On Tue, Nov 13, 2012 at 5:06 PM, Wei Mi <wmi@google.com> wrote:
> Thanks for catching this. I update the patch.
>
> Regards,
> Wei.
>
> On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson <rth@redhat.com> wrote:
>> On 11/13/2012 04:08 PM, Wei Mi wrote:
>>> +extern void tsan_finish_file (void);
>>> +
>>> +#endif /* TREE_TSAN */
>>> +/* ThreadSanitizer, a data race detector.
>>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>>
>> Careful, you've got double applied patches there.
>>
>>
>> r~

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-16 23:13                       ` Wei Mi
@ 2012-11-18 18:52                         ` Konstantin Serebryany
  2012-11-22  7:23                           ` Wei Mi
  0 siblings, 1 reply; 56+ messages in thread
From: Konstantin Serebryany @ 2012-11-18 18:52 UTC (permalink / raw)
  To: Wei Mi
  Cc: Richard Henderson, Jakub Jelinek, GCC Patches, David Li,
	Diego Novillo, Dodji Seketeli, Dmitry Vyukov

Just a comment about tsan.
Today, tsan works *only* on x86_64 linux (no 32-bits, no non-linux).
Other 64-bit platforms may be doable, but not as easy as for asan.
Non-linux is harder than non-x86_64 (need to support tons of libc interceptors).
32-bit platforms are very hard to port to, I would not bother for now.
(this probably includes x32, which has cheap atomic 64-bit
loads/stores, but has too small address space for tsan)

Conclusion: when committing tsan code, please make sure it is enable
only on x86_64

--kcc

On Sat, Nov 17, 2012 at 3:13 AM, Wei Mi <wmi@google.com> wrote:
> Hi,
>
> Is it ok for the trunk?
>
> Thanks,
> Wei.
>
> On Tue, Nov 13, 2012 at 5:06 PM, Wei Mi <wmi@google.com> wrote:
>> Thanks for catching this. I update the patch.
>>
>> Regards,
>> Wei.
>>
>> On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson <rth@redhat.com> wrote:
>>> On 11/13/2012 04:08 PM, Wei Mi wrote:
>>>> +extern void tsan_finish_file (void);
>>>> +
>>>> +#endif /* TREE_TSAN */
>>>> +/* ThreadSanitizer, a data race detector.
>>>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>>>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>>>
>>> Careful, you've got double applied patches there.
>>>
>>>
>>> r~

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-18 18:52                         ` Konstantin Serebryany
@ 2012-11-22  7:23                           ` Wei Mi
       [not found]                             ` <CACT4Y+aR5vizMRb7VCXK7w=bnEz29g9WjQkjvu=8=kds610D1Q@mail.gmail.com>
  2012-11-22  9:54                             ` [tsan] ThreadSanitizer instrumentation part Jakub Jelinek
  0 siblings, 2 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-22  7:23 UTC (permalink / raw)
  To: Konstantin Serebryany
  Cc: Richard Henderson, Jakub Jelinek, GCC Patches, David Li,
	Diego Novillo, Dodji Seketeli, Dmitry Vyukov

[-- Attachment #1: Type: text/plain, Size: 2467 bytes --]

Hi,

I update the tsan patch against trunk, and create libtsan patch.
Please see if it is ok.

gcc/ChangeLog:
2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
            Wei Mi  <wmi@google.com>

        * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
        * sanitizer.def: Ditto.
        * Makefile.in (tsan.o): Add tsan.o target.
        (BUILTINS_DEF): Add sanitizer.def.
        * passes.c (init_optimization_passes): Add tsan passes.
        * tree-pass.h (register_pass_info): Ditto.
        * toplev.c (compile_file): Ditto.
        * doc/invoke.texi: Document tsan related options.
        * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
if -fsanitize=thread.
        * tsan.c: New file about tsan.
        * tsan.h: Ditto.
        * common.opt: Add -fsanitize=thread.

libsanitizer/ChangeLog:
2012-11-22  Wei Mi  <wmi@google.com>

        * tsan: New directory. Import tsan runtime from llvm.
        * configure.ac: Add 64 bits tsan build.
        * Makefile.am: Likewise.
        * configure: Regenerated.
        * Makefile.in: Likewise.

Thanks,
Wei.

On Sun, Nov 18, 2012 at 10:52 AM, Konstantin Serebryany
<konstantin.s.serebryany@gmail.com> wrote:
> Just a comment about tsan.
> Today, tsan works *only* on x86_64 linux (no 32-bits, no non-linux).
> Other 64-bit platforms may be doable, but not as easy as for asan.
> Non-linux is harder than non-x86_64 (need to support tons of libc interceptors).
> 32-bit platforms are very hard to port to, I would not bother for now.
> (this probably includes x32, which has cheap atomic 64-bit
> loads/stores, but has too small address space for tsan)
>
> Conclusion: when committing tsan code, please make sure it is enable
> only on x86_64
>
> --kcc
>
> On Sat, Nov 17, 2012 at 3:13 AM, Wei Mi <wmi@google.com> wrote:
>> Hi,
>>
>> Is it ok for the trunk?
>>
>> Thanks,
>> Wei.
>>
>> On Tue, Nov 13, 2012 at 5:06 PM, Wei Mi <wmi@google.com> wrote:
>>> Thanks for catching this. I update the patch.
>>>
>>> Regards,
>>> Wei.
>>>
>>> On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson <rth@redhat.com> wrote:
>>>> On 11/13/2012 04:08 PM, Wei Mi wrote:
>>>>> +extern void tsan_finish_file (void);
>>>>> +
>>>>> +#endif /* TREE_TSAN */
>>>>> +/* ThreadSanitizer, a data race detector.
>>>>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>>>>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>>>>
>>>> Careful, you've got double applied patches there.
>>>>
>>>>
>>>> r~

[-- Attachment #2: patch.tsan.txt --]
[-- Type: text/plain, Size: 23035 bytes --]

Index: gcc/gcc.c
===================================================================
--- gcc/gcc.c	(revision 193702)
+++ gcc/gcc.c	(working copy)
@@ -553,6 +553,15 @@ proper position among the other output f
 #endif
 #endif
 
+#ifndef LIBTSAN_SPEC
+#ifdef HAVE_LD_STATIC_DYNAMIC
+#define LIBTSAN_SPEC "%{static-libtsan:" LD_STATIC_OPTION \
+		     "} -ltsan %{static-libtsan:" LD_DYNAMIC_OPTION "}"
+#else
+#define LIBTSAN_SPEC "-ltsan"
+#endif
+#endif
+
 /* config.h can define LIBGCC_SPEC to override how and when libgcc.a is
    included.  */
 #ifndef LIBGCC_SPEC
@@ -697,6 +706,7 @@ proper position among the other output f
     %(mflib) " STACK_SPLIT_SPEC "\
     %{fprofile-arcs|fprofile-generate*|coverage:-lgcov}\
     %{fsanitize=address:" LIBASAN_SPEC "}\
+    %{fsanitize=thread:" LIBTSAN_SPEC "}\
     %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\
     %{!nostdlib:%{!nostartfiles:%E}} %{T*} }}}}}}"
 #endif
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/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 193702)
+++ gcc/Makefile.in	(working copy)
@@ -863,7 +863,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 \
@@ -1365,6 +1365,7 @@ OBJS = \
 	trans-mem.o \
 	tree-affine.o \
 	asan.o \
+	tsan.o \
 	tree-call-cdce.o \
 	tree-cfg.o \
 	tree-cfgcleanup.o \
@@ -2228,6 +2229,12 @@ asan.o : asan.c asan.h $(CONFIG_H) $(SYS
    output.h coretypes.h $(GIMPLE_PRETTY_PRINT_H) \
    tree-iterator.h $(TREE_FLOW_H) $(TREE_PASS_H) \
    $(TARGET_H) $(EXPR_H) $(OPTABS_H) $(TM_P_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-ssa-tail-merge.o: tree-ssa-tail-merge.c \
    $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \
    $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \
@@ -2689,7 +2696,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)
 
@@ -3740,6 +3748,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp
   $(srcdir)/target-globals.h \
   $(srcdir)/ipa-inline.h \
   $(srcdir)/asan.c \
+  $(srcdir)/tsan.c \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 193702)
+++ gcc/doc/invoke.texi	(working copy)
@@ -453,7 +453,8 @@ Objective-C and Objective-C++ Dialects}.
 @xref{Link Options,,Options for Linking}.
 @gccoptlist{@var{object-file-name}  -l@var{library} @gol
 -nostartfiles  -nodefaultlibs  -nostdlib -pie -rdynamic @gol
--s  -static -static-libgcc -static-libasan -static-libstdc++ @gol
+-s  -static -static-libgcc -static-libstdc++ @gol
+-static-libasan -static-libtsan @gol
 -shared -shared-libgcc  -symbolic @gol
 -T @var{script}  -Wl,@var{option}  -Xlinker @var{option} @gol
 -u @var{symbol}}
@@ -6862,6 +6863,12 @@ Memory access instructions will be instr
 out-of-bounds and use-after-free bugs. So far only heap bugs will be detected.
 See @uref{http://code.google.com/p/address-sanitizer/} for more details.
 
+@item -fsanitize=thread
+Enable ThreadSanitizer, a fast data race detector.
+Memory access instructions will be instrumented to detect
+data race bugs. 
+See @uref{http://code.google.com/p/data-race-test/wiki/ThreadSanitizer} for more details.
+
 @item -fmudflap -fmudflapth -fmudflapir
 @opindex fmudflap
 @opindex fmudflapth
@@ -9947,6 +9954,15 @@ option is not used, then this links agai
 driver to link @file{libasan} statically, without necessarily linking
 other libraries statically.
 
+@item -static-libtsan
+When the @option{-fsanitize=thread} option is used to link a program,
+the GCC driver automatically links against @option{libtsan}.  If
+@file{libtsan} is available as a shared library, and the @option{-static}
+option is not used, then this links against the shared version of
+@file{libtsan}.  The @option{-static-libtsan} option directs the GCC
+driver to link @file{libtsan} statically, without necessarily linking
+other libraries statically.
+
 @item -static-libstdc++
 When the @command{g++} program is used to link a C++ program, it
 normally automatically links against @option{libstdc++}.  If
Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 193702)
+++ gcc/passes.c	(working copy)
@@ -1450,6 +1450,7 @@ init_optimization_passes (void)
       NEXT_PASS (pass_pre);
       NEXT_PASS (pass_sink_code);
       NEXT_PASS (pass_asan);
+      NEXT_PASS (pass_tsan);
       NEXT_PASS (pass_tree_loop);
 	{
 	  struct opt_pass **p = &pass_tree_loop.pass.sub;
@@ -1556,6 +1557,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_lower_complex_O0);
   NEXT_PASS (pass_asan_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 193702)
+++ gcc/tree-pass.h	(working copy)
@@ -261,6 +261,8 @@ extern struct gimple_opt_pass pass_mudfl
 extern struct gimple_opt_pass pass_mudflap_2;
 extern struct gimple_opt_pass pass_asan;
 extern struct gimple_opt_pass pass_asan_O0;
+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/tsan.c
===================================================================
--- gcc/tsan.c	(revision 0)
+++ gcc/tsan.c	(revision 0)
@@ -0,0 +1,406 @@
+/* GCC instrumentation plugin for ThreadSanitizer.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<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 "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_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 builtin_decl_implicit (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;
+  tree base, rhs, expr_type, expr_ptr, builtin_decl;
+  basic_block bb;
+  HOST_WIDE_INT size;
+  gimple stmt, g;
+  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;
+
+  size = int_size_in_bytes (TREE_TYPE (expr));
+  if (size == -1)
+    return false;
+
+  /* For now just avoid instrumenting bit field acceses.
+     TODO: handle bit-fields as if touching the whole field.  */
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  get_inner_reference (expr, &bitsize, &bitpos, &offset,
+		       &mode, &unsignedp, &volatilep, false);
+  if (bitpos % (size * BITS_PER_UNIT)
+      || bitsize != size * BITS_PER_UNIT)
+    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_fold_addr_expr (unshare_expr (expr));
+  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 = builtin_decl_implicit (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 basic block, so the instrumented stmts need to be
+	 inserted in successor bbs. */
+      if (is_ctrl_altering_stmt (stmt))
+	{
+	  edge e;
+
+	  bb = gsi_bb (gsi);
+	  e = find_fallthru_edge (bb->succs);
+	  if (e)
+	    gsi_insert_seq_on_edge_immediate (e, g);
+	}
+      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)
+	  != builtin_decl_implicit (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 = builtin_decl_implicit (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 =  builtin_decl_implicit (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 = builtin_decl_implicit (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
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+/* 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 = builtin_decl_implicit (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  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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
+	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+}
+
+struct gimple_opt_pass pass_tsan_O0 =
+{
+ {
+  GIMPLE_PASS,
+  "tsan0",				/* name  */
+  OPTGROUP_NONE,			/* optinfo_flags */
+  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, 2012 Free Software Foundation, Inc.
+   Contributed by Dmitry Vyukov <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef TREE_TSAN
+#define TREE_TSAN
+
+extern void tsan_finish_file (void);
+
+#endif /* TREE_TSAN */
Index: gcc/common.opt
===================================================================
--- gcc/common.opt	(revision 193702)
+++ gcc/common.opt	(working copy)
@@ -844,6 +844,10 @@ fsanitize=address
 Common Report Var(flag_asan)
 Enable AddressSanitizer, a memory error detector
 
+fsanitize=thread
+Common Report Var(flag_tsan)
+Enable ThreadSanitizer, a data race detector
+
 fasynchronous-unwind-tables
 Common Report Var(flag_asynchronous_unwind_tables) Optimization
 Generate unwind tables that are exact at each instruction boundary
@@ -2519,6 +2523,9 @@ Driver
 static-libasan
 Driver
 
+static-libtsan
+Driver
+
 symbolic
 Driver
 
Index: gcc/toplev.c
===================================================================
--- gcc/toplev.c	(revision 193702)
+++ 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 "asan.h"
+#include "tsan.h"
 #include "gimple.h"
 #include "tree-ssa-alias.h"
 #include "plugin.h"
@@ -575,6 +576,9 @@ compile_file (void)
       if (flag_asan)
         asan_finish_file ();
 
+      if (flag_tsan)
+	tsan_finish_file ();
+
       output_shared_constant_pool ();
       output_object_blocks ();
       finish_tm_clone_pairs ();
Index: gcc/builtins.def
===================================================================
--- gcc/builtins.def	(revision 193702)
+++ gcc/builtins.def	(working copy)
@@ -149,6 +149,15 @@ 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 
+   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 +834,7 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE",
 
 /* GTM builtins. */
 #include "gtm-builtins.def"
+
+/* Sanitizer builtins. */
+#include "sanitizer.def"
+

[-- Attachment #3: patch.libtsan.txt.bz2 --]
[-- Type: application/x-bzip2, Size: 53750 bytes --]

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]                               ` <CACT4Y+YrUg13dqtUTD23qEWnySNgQ1T_H06R2=ytpXKZeG8UUQ@mail.gmail.com>
@ 2012-11-22  7:45                                 ` Xinliang David Li
       [not found]                                   ` <CACT4Y+a07z9-VtGKQ7gGCyHWb_XH4sVWFtPG=qpkk_gJXfc40A@mail.gmail.com>
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-22  7:45 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Wei Mi, Konstantin Serebryany, Richard Henderson, Jakub Jelinek,
	GCC Patches, Diego Novillo, Dodji Seketeli

On Wed, Nov 21, 2012 at 11:35 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> What percent of the memory accesses the following can skip?
>
> I just don't know what exactly they mean. ADDR_EXPR/COMPONENT_REF look like
> it can skip a lot.
>

It does not skip a lot.

>
> +  /* TODO: handle other cases
> +     (FIELD_DECL, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */

The comment is not correct.  The analysis should not care about
FIELD_DECL (covered by COMPONENT_REF), nor ADDR_EXPR.  Due to the pass
ordering, target mem-ref is not seen either.


> +  if (tcode != ARRAY_REF
> +      && tcode != VAR_DECL
> +      && tcode != COMPONENT_REF
> +      && tcode != INDIRECT_REF
> +      && tcode != MEM_REF)
> +    return false;
>

The listed cases are handled.

David

>
>
> On Thu, Nov 22, 2012 at 11:29 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
>>
>> +static bool
>> +tsan_gate (void)
>> +{
>> +  return flag_tsan != 0
>> + && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
>>
>>
>> What does it mean? Why builtin_decl_implicit_p (BUILT_IN_TSAN_INIT)?
>>
>> +}
>>
>>
>>
>> On Thu, Nov 22, 2012 at 11:22 AM, Wei Mi <wmi@google.com> wrote:
>>>
>>> Hi,
>>>
>>> I update the tsan patch against trunk, and create libtsan patch.
>>> Please see if it is ok.
>>>
>>> gcc/ChangeLog:
>>> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>>>             Wei Mi  <wmi@google.com>
>>>
>>>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>>>         * sanitizer.def: Ditto.
>>>         * Makefile.in (tsan.o): Add tsan.o target.
>>>         (BUILTINS_DEF): Add sanitizer.def.
>>>         * passes.c (init_optimization_passes): Add tsan passes.
>>>         * tree-pass.h (register_pass_info): Ditto.
>>>         * toplev.c (compile_file): Ditto.
>>>         * doc/invoke.texi: Document tsan related options.
>>>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
>>> if -fsanitize=thread.
>>>         * tsan.c: New file about tsan.
>>>         * tsan.h: Ditto.
>>>         * common.opt: Add -fsanitize=thread.
>>>
>>> libsanitizer/ChangeLog:
>>> 2012-11-22  Wei Mi  <wmi@google.com>
>>>
>>>         * tsan: New directory. Import tsan runtime from llvm.
>>>         * configure.ac: Add 64 bits tsan build.
>>>         * Makefile.am: Likewise.
>>>         * configure: Regenerated.
>>>         * Makefile.in: Likewise.
>>>
>>> Thanks,
>>> Wei.
>>>
>>> On Sun, Nov 18, 2012 at 10:52 AM, Konstantin Serebryany
>>> <konstantin.s.serebryany@gmail.com> wrote:
>>> > Just a comment about tsan.
>>> > Today, tsan works *only* on x86_64 linux (no 32-bits, no non-linux).
>>> > Other 64-bit platforms may be doable, but not as easy as for asan.
>>> > Non-linux is harder than non-x86_64 (need to support tons of libc
>>> > interceptors).
>>> > 32-bit platforms are very hard to port to, I would not bother for now.
>>> > (this probably includes x32, which has cheap atomic 64-bit
>>> > loads/stores, but has too small address space for tsan)
>>> >
>>> > Conclusion: when committing tsan code, please make sure it is enable
>>> > only on x86_64
>>> >
>>> > --kcc
>>> >
>>> > On Sat, Nov 17, 2012 at 3:13 AM, Wei Mi <wmi@google.com> wrote:
>>> >> Hi,
>>> >>
>>> >> Is it ok for the trunk?
>>> >>
>>> >> Thanks,
>>> >> Wei.
>>> >>
>>> >> On Tue, Nov 13, 2012 at 5:06 PM, Wei Mi <wmi@google.com> wrote:
>>> >>> Thanks for catching this. I update the patch.
>>> >>>
>>> >>> Regards,
>>> >>> Wei.
>>> >>>
>>> >>> On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson <rth@redhat.com>
>>> >>> wrote:
>>> >>>> On 11/13/2012 04:08 PM, Wei Mi wrote:
>>> >>>>> +extern void tsan_finish_file (void);
>>> >>>>> +
>>> >>>>> +#endif /* TREE_TSAN */
>>> >>>>> +/* ThreadSanitizer, a data race detector.
>>> >>>>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>>> >>>>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>>> >>>>
>>> >>>> Careful, you've got double applied patches there.
>>> >>>>
>>> >>>>
>>> >>>> r~
>>
>>
>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]                             ` <CACT4Y+aR5vizMRb7VCXK7w=bnEz29g9WjQkjvu=8=kds610D1Q@mail.gmail.com>
       [not found]                               ` <CACT4Y+YrUg13dqtUTD23qEWnySNgQ1T_H06R2=ytpXKZeG8UUQ@mail.gmail.com>
@ 2012-11-22  8:08                               ` Jakub Jelinek
  2012-11-22 15:13                                 ` [PATCH] Convert asan to use sanitizer.def builtins, initialize them if the FE didn't Jakub Jelinek
  1 sibling, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-22  8:08 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Wei Mi, Konstantin Serebryany, Richard Henderson, GCC Patches,
	David Li, Diego Novillo, Dodji Seketeli

On Thu, Nov 22, 2012 at 11:29:05AM +0400, Dmitry Vyukov wrote:
> +static bool
> +tsan_gate (void)
> +{
> +  return flag_tsan != 0
> +	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
> 
> 
> What does it mean? Why builtin_decl_implicit_p (BUILT_IN_TSAN_INIT)?

It is a temporary workaround, I'll handle it when the patch goes in.
The thing is that while the C/C++ family of FEs will create all the builtins
just because they are included in builtins.def, other FEs won't.
So we need some routine that will build the builtins if the FEs didn't
initialize them.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22  7:23                           ` Wei Mi
       [not found]                             ` <CACT4Y+aR5vizMRb7VCXK7w=bnEz29g9WjQkjvu=8=kds610D1Q@mail.gmail.com>
@ 2012-11-22  9:54                             ` Jakub Jelinek
  2012-11-22 13:21                               ` [tsan] libsanitizer tweaks Jakub Jelinek
  2012-11-22 22:08                               ` [tsan] ThreadSanitizer instrumentation part Wei Mi
  1 sibling, 2 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-22  9:54 UTC (permalink / raw)
  To: Wei Mi
  Cc: Konstantin Serebryany, Richard Henderson, GCC Patches, David Li,
	Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Wed, Nov 21, 2012 at 11:22:51PM -0800, Wei Mi wrote:
> I update the tsan patch against trunk, and create libtsan patch.
> Please see if it is ok.
> 
> gcc/ChangeLog:
> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>             Wei Mi  <wmi@google.com>
> 
>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>         * sanitizer.def: Ditto.
>         * Makefile.in (tsan.o): Add tsan.o target.
>         (BUILTINS_DEF): Add sanitizer.def.
>         * passes.c (init_optimization_passes): Add tsan passes.
>         * tree-pass.h (register_pass_info): Ditto.
>         * toplev.c (compile_file): Ditto.
>         * doc/invoke.texi: Document tsan related options.
>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
> if -fsanitize=thread.
>         * tsan.c: New file about tsan.
>         * tsan.h: Ditto.
>         * common.opt: Add -fsanitize=thread.
> 
> libsanitizer/ChangeLog:
> 2012-11-22  Wei Mi  <wmi@google.com>
> 
>         * tsan: New directory. Import tsan runtime from llvm.
>         * configure.ac: Add 64 bits tsan build.
>         * Makefile.am: Likewise.
>         * configure: Regenerated.
>         * Makefile.in: Likewise.

Ok, thanks.  The comments can be fixed up incrementally.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [tsan] libsanitizer tweaks
  2012-11-22  9:54                             ` [tsan] ThreadSanitizer instrumentation part Jakub Jelinek
@ 2012-11-22 13:21                               ` Jakub Jelinek
  2012-11-22 23:22                                 ` Diego Novillo
  2012-11-22 22:08                               ` [tsan] ThreadSanitizer instrumentation part Wei Mi
  1 sibling, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-22 13:21 UTC (permalink / raw)
  To: Wei Mi, Dodji Seketeli
  Cc: Konstantin Serebryany, GCC Patches, David Li, Diego Novillo,
	Dmitry Vyukov

On Thu, Nov 22, 2012 at 10:54:00AM +0100, Jakub Jelinek wrote:
> Ok, thanks.  The comments can be fixed up incrementally.

I had closer look at the libsanitizer/tsan configury, and I think I'd prefer
the configure.tgt script to say if tsan should be built or not.
Furthermore, GCC doesn't support -Wno-c99-extensions flag for C++, that is a
clang-ism.
Ok for trunk (after the tsan patches are committed, on top of them)?

2012-11-22  Jakub Jelinek  <jakub@redhat.com>

	* tsan/Makefile.am (AM_CXXFLAGS): Remove -Wno-variadic-macros.
	* Makefile.am (SUBDIRS): Guard tsan addition with TSAN_SUPPORTED
	automake conditional instead of !MULTISUBDIR32.
	* configure.tgt: Set TSAN_SUPPORTED=yes for x86_64/i686-linux
	for 64-bit multilib.
	* configure.ac: Check for void * size, source in configure.tgt,
	define TSAN_SUPPORTED conditional instead of MULTILIBDIR32.
	* configure: Regenerated.
	* Makefile.in: Regenerated.
	* tsan/Makefile.in: Regenerated.

--- libsanitizer/tsan/Makefile.am.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/tsan/Makefile.am	2012-11-22 13:44:51.898192821 +0100
@@ -1,7 +1,7 @@
 AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
 
 DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
-AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions 
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
 ACLOCAL_AMFLAGS = -I m4
 
 toolexeclib_LTLIBRARIES = libtsan.la
--- libsanitizer/Makefile.am.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/Makefile.am	2012-11-22 14:03:36.947775816 +0100
@@ -1,9 +1,9 @@
 ACLOCAL_AMFLAGS = -I .. -I ../config
 
-if MULTISUBDIR32 
-SUBDIRS = interception sanitizer_common asan 
-else
+if TSAN_SUPPORTED
 SUBDIRS = interception sanitizer_common asan tsan 
+else
+SUBDIRS = interception sanitizer_common asan 
 endif
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
--- libsanitizer/configure.tgt.jj	2012-11-16 12:39:18.000000000 +0100
+++ libsanitizer/configure.tgt	2012-11-22 14:11:31.233300510 +0100
@@ -20,7 +20,12 @@
 
 # Filter out unsupported systems.
 case "${target}" in
-  x86_64-*-linux* | i?86-*-linux* | sparc*-*-linux*)
+  x86_64-*-linux* | i?86-*-linux*)
+	if test x$ac_cv_sizeof_void_p = x8; then
+		TSAN_SUPPORTED=yes
+	fi
+	;;
+  sparc*-*-linux*)
 	;;
   *)
 	UNSUPPORTED=1
--- libsanitizer/configure.ac.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/configure.ac	2012-11-22 13:59:46.457084798 +0100
@@ -67,12 +67,18 @@ AM_PROG_LIBTOOL
 AC_SUBST(enable_shared)
 AC_SUBST(enable_static)
 
+AC_CHECK_SIZEOF([void *])
+
 if test "${multilib}" = "yes"; then
   multilib_arg="--enable-multilib"
 else
   multilib_arg=
 fi
-AM_CONDITIONAL(MULTISUBDIR32, [test "x$with_multisubdir" = "x32"])
+
+# Get target configury.
+unset TSAN_SUPPORTED
+. ${srcdir}/configure.tgt
+AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
 
 AC_CONFIG_FILES([Makefile])
 
@@ -89,7 +95,7 @@ _EOF
    AS_UNSET([ml_norecursion])
 ])
 
-if test "x$with_multisubdir" != "x32"; then
+if test "x$TSAN_SUPPORTED" = "xyes"; then
   AC_CONFIG_FILES(AC_FOREACH([DIR], [tsan], [DIR/Makefile ]), 
     [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
--- libsanitizer/configure.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/configure	2012-11-22 14:01:47.463398176 +0100
@@ -604,8 +604,8 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
-MULTISUBDIR32_FALSE
-MULTISUBDIR32_TRUE
+TSAN_SUPPORTED_FALSE
+TSAN_SUPPORTED_TRUE
 enable_static
 enable_shared
 CXXCPP
@@ -1878,6 +1878,184 @@ fi
   return $ac_retval
 
 } # ac_fn_cxx_try_link
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+			if test $ac_lo -le $ac_mid; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+			if test $ac_mid -le $ac_hi; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+  return $ac_retval
+
+} # ac_fn_c_compute_int
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -10900,7 +11078,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 10903 "configure"
+#line 11081 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11006,7 +11184,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11009 "configure"
+#line 11187 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -14263,17 +14441,56 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+$as_echo_n "checking size of void *... " >&6; }
+if test "${ac_cv_sizeof_void_p+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_void_p" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (void *)
+See \`config.log' for more details." "$LINENO" 5; }; }
+   else
+     ac_cv_sizeof_void_p=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+$as_echo "$ac_cv_sizeof_void_p" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_VOID_P $ac_cv_sizeof_void_p
+_ACEOF
+
+
+
 if test "${multilib}" = "yes"; then
   multilib_arg="--enable-multilib"
 else
   multilib_arg=
 fi
- if test "x$with_multisubdir" = "x32"; then
-  MULTISUBDIR32_TRUE=
-  MULTISUBDIR32_FALSE='#'
+
+# Get target configury.
+unset TSAN_SUPPORTED
+. ${srcdir}/configure.tgt
+ if test "x$TSAN_SUPPORTED" = "xyes"; then
+  TSAN_SUPPORTED_TRUE=
+  TSAN_SUPPORTED_FALSE='#'
 else
-  MULTISUBDIR32_TRUE='#'
-  MULTISUBDIR32_FALSE=
+  TSAN_SUPPORTED_TRUE='#'
+  TSAN_SUPPORTED_FALSE=
 fi
 
 
@@ -14283,7 +14500,7 @@ ac_config_files="$ac_config_files Makefi
 ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile"
 
 
-if test "x$with_multisubdir" != "x32"; then
+if test "x$TSAN_SUPPORTED" = "xyes"; then
   ac_config_files="$ac_config_files tsan/Makefile"
 
 fi
@@ -14449,8 +14666,8 @@ if test -z "${am__fastdepCCAS_TRUE}" &&
   as_fn_error "conditional \"am__fastdepCCAS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${MULTISUBDIR32_TRUE}" && test -z "${MULTISUBDIR32_FALSE}"; then
-  as_fn_error "conditional \"MULTISUBDIR32\" was never defined.
+if test -z "${TSAN_SUPPORTED_TRUE}" && test -z "${TSAN_SUPPORTED_FALSE}"; then
+  as_fn_error "conditional \"TSAN_SUPPORTED\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 
--- libsanitizer/tsan/Makefile.in.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/tsan/Makefile.in	2012-11-22 14:04:19.902532612 +0100
@@ -238,7 +238,7 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
-AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions 
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
 ACLOCAL_AMFLAGS = -I m4
 toolexeclib_LTLIBRARIES = libtsan.la
 tsan_files = \
--- libsanitizer/Makefile.in.jj	2012-11-22 13:18:04.000000000 +0100
+++ libsanitizer/Makefile.in	2012-11-22 14:04:19.928532489 +0100
@@ -244,8 +244,8 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 ACLOCAL_AMFLAGS = -I .. -I ../config
-@MULTISUBDIR32_FALSE@SUBDIRS = interception sanitizer_common asan tsan 
-@MULTISUBDIR32_TRUE@SUBDIRS = interception sanitizer_common asan 
+@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan 
+@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan 
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
 # values defined in terms of make variables, as is the case for CC and


	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH] Convert asan to use sanitizer.def builtins, initialize them if the FE didn't
  2012-11-22  8:08                               ` Jakub Jelinek
@ 2012-11-22 15:13                                 ` Jakub Jelinek
  2012-12-03 16:58                                   ` Dodji Seketeli
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-22 15:13 UTC (permalink / raw)
  To: Dmitry Vyukov, Dodji Seketeli
  Cc: Wei Mi, Konstantin Serebryany, GCC Patches, David Li, Diego Novillo

On Thu, Nov 22, 2012 at 09:08:23AM +0100, Jakub Jelinek wrote:
> On Thu, Nov 22, 2012 at 11:29:05AM +0400, Dmitry Vyukov wrote:
> > +static bool
> > +tsan_gate (void)
> > +{
> > +  return flag_tsan != 0
> > +	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
> > 
> > 
> > What does it mean? Why builtin_decl_implicit_p (BUILT_IN_TSAN_INIT)?
> 
> It is a temporary workaround, I'll handle it when the patch goes in.
> The thing is that while the C/C++ family of FEs will create all the builtins
> just because they are included in builtins.def, other FEs won't.
> So we need some routine that will build the builtins if the FEs didn't
> initialize them.

Like so.  The c-family/ FEs (C/ObjC/C++/ObjC++) will initialize the builtins
themselves automatically, for other FEs that don't there is a new routine
that initializes them the first time they are needed.

Ok for trunk (the patch is on top of the tsan patch)?

2012-11-22  Jakub Jelinek  <jakub@redhat.com>

	* sanitizer.def: Add Address Sanitizer builtins.
	Rename BUILT_IN_TSAN_READ_* to BUILT_IN_TSAN_READ* and
	BUILT_IN_TSAN_WRITE_* to BUILT_IN_TSAN_WRITE*.
	* Makefile.in (asan.o): Depend on langhooks.h.
	(tsan.o): Depend on asan.h.
	* asan.h (initialize_sanitizer_builtins): New prototype.
	* asan.c: Include langhooks.h.
	(report_error_func): Use builtin_decl_implicit of corresponding
	BUILT_IN_ASAN_REPORT_{LOAD,STORE}*.
	(asan_init_func): Removed.
	(initialize_sanitizer_builtins): New function.
	(asan_finish_file): Call it.  Use builtin_decl_implicit
	on BUILT_IN_ASAN_{INIT,{,UN}REGISTER_GLOBALS}.
	(asan_instrument): Call initialize_sanitizer_builtins.
	* builtins.def (DEF_SANITIZER_BUILTIN): Change condition to
	(flag_asan || flag_tsan).
	* tsan.c: Include asan.h and tsan.h.
	(get_memory_access_decl): Rename BUILT_IN_TSAN_{READ,WRITE}_*
	to BUILT_IN_TSAN_{READ,WRITE}*.
	(tsan_pass): Call initialize_sanitizer_builtins.
	(tsan_gate, tsan_gate_O0): Don't check if
	builtin_decl_implicit_p (BUILT_IN_TSAN_INIT) is true.
	(tsan_finish_file): Call initialize_sanitizer_builtins.
	* builtin-types.def (BT_FN_VOID_PTR_PTRMODE): New fn type.

--- gcc/sanitizer.def.jj	2012-11-22 13:17:24.000000000 +0100
+++ gcc/sanitizer.def	2012-11-22 15:45:55.873655417 +0100
@@ -1,3 +1,34 @@
+/* Address Sanitizer */
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init",
+		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD1, "__asan_report_load1",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD2, "__asan_report_load2",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD4, "__asan_report_load4",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD8, "__asan_report_load8",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD16, "__asan_report_load16",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE1, "__asan_report_store1",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE2, "__asan_report_store2",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE4, "__asan_report_store4",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE8, "__asan_report_store8",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE16, "__asan_report_store16",
+		      BT_FN_VOID_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REGISTER_GLOBALS,
+		      "__asan_register_globals",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNREGISTER_GLOBALS,
+		      "__asan_unregister_globals",
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
+
+/* Thread Sanitizer */
 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",
@@ -6,23 +37,23 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_FUNC
 		      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",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ1, "__tsan_read1",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_2, "__tsan_read2",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ2, "__tsan_read2",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_4, "__tsan_read4",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ4, "__tsan_read4",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_8, "__tsan_read8",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ8, "__tsan_read8",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ_16, "__tsan_read16",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_READ16, "__tsan_read16",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_1, "__tsan_write1",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE1, "__tsan_write1",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_2, "__tsan_write2",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE2, "__tsan_write2",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_4, "__tsan_write4",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE4, "__tsan_write4",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_8, "__tsan_write8",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE8, "__tsan_write8",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE_16, "__tsan_write16",
+DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_WRITE16, "__tsan_write16",
 		      BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
--- gcc/Makefile.in.jj	2012-11-22 13:17:24.000000000 +0100
+++ gcc/Makefile.in	2012-11-22 15:41:09.700285758 +0100
@@ -2228,13 +2228,13 @@ stor-layout.o : stor-layout.c $(CONFIG_H
 asan.o : asan.c asan.h $(CONFIG_H) $(SYSTEM_H) $(GIMPLE_H) \
    output.h coretypes.h $(GIMPLE_PRETTY_PRINT_H) \
    tree-iterator.h $(TREE_FLOW_H) $(TREE_PASS_H) \
-   $(TARGET_H) $(EXPR_H) $(OPTABS_H) $(TM_P_H)
+   $(TARGET_H) $(EXPR_H) $(OPTABS_H) $(TM_P_H) langhooks.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
+   intl.h cfghooks.h output.h options.h c-family/c-common.h tsan.h asan.h
 tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \
    $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \
    $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) \
--- gcc/asan.h.jj	2012-11-12 16:58:41.000000000 +0100
+++ gcc/asan.h	2012-11-22 15:13:10.590930008 +0100
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.
 extern void asan_finish_file (void);
 extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int);
 extern bool asan_protect_global (tree);
+extern void initialize_sanitizer_builtins (void);
 
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
--- gcc/asan.c.jj	2012-11-22 11:35:47.000000000 +0100
+++ gcc/asan.c	2012-11-22 15:46:08.326588664 +0100
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.
 #include "optabs.h"
 #include "output.h"
 #include "tm_p.h"
+#include "langhooks.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -485,38 +486,16 @@ asan_protect_global (tree decl)
 static tree
 report_error_func (bool is_store, int size_in_bytes)
 {
-  tree fn_type;
-  tree def;
-  char name[100];
-
-  sprintf (name, "__asan_report_%s%d",
-	   is_store ? "store" : "load", size_in_bytes);
-  fn_type = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
-  def = build_fn_decl (name, fn_type);
-  TREE_NOTHROW (def) = 1;
-  DECL_IGNORED_P (def) = 1;
-  TREE_THIS_VOLATILE (def) = 1;  /* Attribute noreturn. Surprise!  */
-  DECL_ATTRIBUTES (def) = tree_cons (get_identifier ("leaf"),
-				     NULL, DECL_ATTRIBUTES (def));
-  return def;
+  static enum built_in_function report[2][5]
+    = { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
+	  BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
+	  BUILT_IN_ASAN_REPORT_LOAD16 },
+	{ BUILT_IN_ASAN_REPORT_STORE1, BUILT_IN_ASAN_REPORT_STORE2,
+	  BUILT_IN_ASAN_REPORT_STORE4, BUILT_IN_ASAN_REPORT_STORE8,
+	  BUILT_IN_ASAN_REPORT_STORE16 } };
+  return builtin_decl_implicit (report[is_store][exact_log2 (size_in_bytes)]);
 }
 
-/* Construct a function tree for __asan_init().  */
-
-static tree
-asan_init_func (void)
-{
-  tree fn_type;
-  tree def;
-
-  fn_type = build_function_type_list (void_type_node, NULL_TREE);
-  def = build_fn_decl ("__asan_init", fn_type);
-  TREE_NOTHROW (def) = 1;
-  DECL_IGNORED_P (def) = 1;
-  return def;
-}
-
-
 #define PROB_VERY_UNLIKELY	(REG_BR_PROB_BASE / 2000 - 1)
 #define PROB_ALWAYS		(REG_BR_PROB_BASE)
 
@@ -1489,6 +1468,38 @@ asan_add_global (tree decl, tree type, v
   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
 }
 
+/* Initialize sanitizer.def builtins if the FE hasn't initialized them.  */
+void
+initialize_sanitizer_builtins (void)
+{
+  tree decl;
+
+  if (builtin_decl_implicit (BUILT_IN_ASAN_INIT))
+    return;
+
+  tree BT_FN_VOID = build_function_type_list (void_type_node, NULL_TREE);
+  tree BT_FN_VOID_PTR
+    = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
+  tree BT_FN_VOID_PTR_PTRMODE
+    = build_function_type_list (void_type_node, ptr_type_node,
+				build_nonstandard_integer_type (POINTER_SIZE,
+								1), NULL_TREE);
+#undef ATTR_NOTHROW_LEAF_LIST
+#define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
+#undef ATTR_NORETURN_NOTHROW_LEAF_LIST
+#define ATTR_NORETURN_NOTHROW_LEAF_LIST ECF_NORETURN | ATTR_NOTHROW_LEAF_LIST
+#undef DEF_SANITIZER_BUILTIN
+#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
+  decl = add_builtin_function ("__builtin_" NAME, TYPE, ENUM,		\
+			       BUILT_IN_NORMAL, NAME, NULL_TREE);	\
+  set_call_expr_flags (decl, ATTRS);					\
+  set_builtin_decl (ENUM, decl, true);
+
+#include "sanitizer.def"
+
+#undef DEF_SANITIZER_BUILTIN
+}
+
 /* Needs to be GTY(()), because cgraph_build_static_cdtor may
    invoke ggc_collect.  */
 static GTY(()) tree asan_ctor_statements;
@@ -1504,14 +1515,16 @@ asan_finish_file (void)
   struct varpool_node *vnode;
   unsigned HOST_WIDE_INT gcount = 0;
 
-  append_to_statement_list (build_call_expr (asan_init_func (), 0),
-			    &asan_ctor_statements);
+  initialize_sanitizer_builtins ();
+
+  tree fn = builtin_decl_implicit (BUILT_IN_ASAN_INIT);
+  append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements);
   FOR_EACH_DEFINED_VARIABLE (vnode)
     if (asan_protect_global (vnode->symbol.decl))
       ++gcount;
   if (gcount)
     {
-      tree type = asan_global_struct (), var, ctor, decl;
+      tree type = asan_global_struct (), var, ctor;
       tree uptr = build_nonstandard_integer_type (POINTER_SIZE, 1);
       tree dtor_statements = NULL_TREE;
       vec<constructor_elt, va_gc> *v;
@@ -1535,20 +1548,14 @@ asan_finish_file (void)
       DECL_INITIAL (var) = ctor;
       varpool_assemble_decl (varpool_node_for_decl (var));
 
-      type = build_function_type_list (void_type_node, ptr_type_node,
-				       uptr, NULL_TREE);
-      decl = build_fn_decl ("__asan_register_globals", type);
-      TREE_NOTHROW (decl) = 1;
-      DECL_IGNORED_P (decl) = 1;
-      append_to_statement_list (build_call_expr (decl, 2,
+      fn = builtin_decl_implicit (BUILT_IN_ASAN_REGISTER_GLOBALS);
+      append_to_statement_list (build_call_expr (fn, 2,
 						 build_fold_addr_expr (var),
 						 build_int_cst (uptr, gcount)),
 				&asan_ctor_statements);
 
-      decl = build_fn_decl ("__asan_unregister_globals", type);
-      TREE_NOTHROW (decl) = 1;
-      DECL_IGNORED_P (decl) = 1;
-      append_to_statement_list (build_call_expr (decl, 2,
+      fn = builtin_decl_implicit (BUILT_IN_ASAN_UNREGISTER_GLOBALS);
+      append_to_statement_list (build_call_expr (fn, 2,
 						 build_fold_addr_expr (var),
 						 build_int_cst (uptr, gcount)),
 				&dtor_statements);
@@ -1579,7 +1586,10 @@ static unsigned int
 asan_instrument (void)
 {
   if (shadow_ptr_types[0] == NULL_TREE)
-    asan_init_shadow_ptr_types ();
+    {
+      asan_init_shadow_ptr_types ();
+      initialize_sanitizer_builtins ();
+    }
   transform_statements ();
   return 0;
 }
--- gcc/builtins.def.jj	2012-11-22 13:17:24.000000000 +0100
+++ gcc/builtins.def	2012-11-22 15:00:04.630358678 +0100
@@ -156,7 +156,7 @@ along with GCC; see the file COPYING3.
 #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)
+	       (flag_asan || flag_tsan))
 
 /* Define an attribute list for math functions that are normally
    "impure" because some of them may write into global memory for
--- gcc/tsan.c.jj	2012-11-22 13:17:24.000000000 +0100
+++ gcc/tsan.c	2012-11-22 15:39:38.080808225 +0100
@@ -37,6 +37,8 @@ along with GCC; see the file COPYING3.
 #include "target.h"
 #include "cgraph.h"
 #include "diagnostic.h"
+#include "tsan.h"
+#include "asan.h"
 
 /* Number of instrumented memory accesses in the current function.  */
 
@@ -49,20 +51,20 @@ get_memory_access_decl (bool is_write, u
   enum built_in_function fcode;
 
   if (size <= 1)
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_1
-		     : BUILT_IN_TSAN_READ_1;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE1
+		     : BUILT_IN_TSAN_READ1;
   else if (size <= 3)
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_2
-		     : BUILT_IN_TSAN_READ_2;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE2
+		     : BUILT_IN_TSAN_READ2;
   else if (size <= 7)
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_4
-		     : BUILT_IN_TSAN_READ_4;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE4
+		     : BUILT_IN_TSAN_READ4;
   else if (size <= 15)
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_8
-		     : BUILT_IN_TSAN_READ_8;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE8
+		     : BUILT_IN_TSAN_READ8;
   else
-    fcode = is_write ? BUILT_IN_TSAN_WRITE_16
-		     : BUILT_IN_TSAN_READ_16;
+    fcode = is_write ? BUILT_IN_TSAN_WRITE16
+		     : BUILT_IN_TSAN_READ16;
 
   return builtin_decl_implicit (fcode);
 }
@@ -321,6 +323,7 @@ instrument_func_exit (void)
 static unsigned
 tsan_pass (void)
 {
+  initialize_sanitizer_builtins ();
   if (instrument_memory_accesses ())
     {
       instrument_func_entry ();
@@ -334,8 +337,7 @@ tsan_pass (void)
 static bool
 tsan_gate (void)
 {
-  return flag_tsan != 0
-	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+  return flag_tsan != 0;
 }
 
 /* Inserts __tsan_init () into the list of CTORs.  */
@@ -343,11 +345,10 @@ tsan_gate (void)
 void
 tsan_finish_file (void)
 {
-  tree ctor_statements;
-  tree init_decl;
+  tree ctor_statements = NULL_TREE;
 
-  ctor_statements = NULL_TREE;
-  init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
+  initialize_sanitizer_builtins ();
+  tree init_decl = builtin_decl_implicit (BUILT_IN_TSAN_INIT);
   append_to_statement_list (build_call_expr (init_decl, 0),
 			    &ctor_statements);
   cgraph_build_static_cdtor ('I', ctor_statements,
@@ -380,8 +381,7 @@ struct gimple_opt_pass pass_tsan =
 static bool
 tsan_gate_O0 (void)
 {
-  return flag_tsan != 0 && !optimize
-	 && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
+  return flag_tsan != 0 && !optimize;
 }
 
 struct gimple_opt_pass pass_tsan_O0 =
--- gcc/builtin-types.def.jj	2012-09-14 14:20:56.000000000 +0200
+++ gcc/builtin-types.def	2012-11-22 14:45:21.918587268 +0100
@@ -253,6 +253,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_INT_FILEP
 		     BT_INT, BT_INT, BT_FILEPTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTRMODE_PTR,
 		     BT_VOID, BT_PTRMODE, BT_PTR)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTRMODE,
+		     BT_VOID, BT_PTR, BT_PTRMODE)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_VALIST_REF_VALIST_ARG,
 		     BT_VOID, BT_VALIST_REF, BT_VALIST_ARG)
 DEF_FUNCTION_TYPE_2 (BT_FN_LONG_LONG_LONG,


	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]                                   ` <CACT4Y+a07z9-VtGKQ7gGCyHWb_XH4sVWFtPG=qpkk_gJXfc40A@mail.gmail.com>
@ 2012-11-22 22:06                                     ` Wei Mi
  0 siblings, 0 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-22 22:06 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Xinliang David Li, Konstantin Serebryany, Richard Henderson,
	Jakub Jelinek, GCC Patches, Diego Novillo, Dodji Seketeli

Thanks, Fixed.

Wei.

On Wed, Nov 21, 2012 at 11:48 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Nov 22, 2012 at 11:45 AM, Xinliang David Li <davidxl@google.com>
> wrote:
>>
>> On Wed, Nov 21, 2012 at 11:35 PM, Dmitry Vyukov <dvyukov@google.com>
>> wrote:
>> > What percent of the memory accesses the following can skip?
>> >
>> > I just don't know what exactly they mean. ADDR_EXPR/COMPONENT_REF look
>> > like
>> > it can skip a lot.
>> >
>>
>> It does not skip a lot.
>>
>> >
>> > +  /* TODO: handle other cases
>> > +     (FIELD_DECL, ARRAY_RANGE_REF, TARGET_MEM_REF, ADDR_EXPR).  */
>>
>> The comment is not correct.  The analysis should not care about
>> FIELD_DECL (covered by COMPONENT_REF), nor ADDR_EXPR.  Due to the pass
>> ordering, target mem-ref is not seen either.
>
>
>
>
> Wei, please update the comment that only ARRAY_RANGE_REF is not handled.
>
>
>> > +  if (tcode != ARRAY_REF
>> > +      && tcode != VAR_DECL
>> > +      && tcode != COMPONENT_REF
>> > +      && tcode != INDIRECT_REF
>> > +      && tcode != MEM_REF)
>> > +    return false;
>> >
>>
>> The listed cases are handled.
>>
>> David
>>
>> >
>> >
>> > On Thu, Nov 22, 2012 at 11:29 AM, Dmitry Vyukov <dvyukov@google.com>
>> > wrote:
>> >>
>> >> +static bool
>> >> +tsan_gate (void)
>> >> +{
>> >> +  return flag_tsan != 0
>> >> + && builtin_decl_implicit_p (BUILT_IN_TSAN_INIT);
>> >>
>> >>
>> >> What does it mean? Why builtin_decl_implicit_p (BUILT_IN_TSAN_INIT)?
>> >>
>> >> +}
>> >>
>> >>
>> >>
>> >> On Thu, Nov 22, 2012 at 11:22 AM, Wei Mi <wmi@google.com> wrote:
>> >>>
>> >>> Hi,
>> >>>
>> >>> I update the tsan patch against trunk, and create libtsan patch.
>> >>> Please see if it is ok.
>> >>>
>> >>> gcc/ChangeLog:
>> >>> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>> >>>             Wei Mi  <wmi@google.com>
>> >>>
>> >>>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>> >>>         * sanitizer.def: Ditto.
>> >>>         * Makefile.in (tsan.o): Add tsan.o target.
>> >>>         (BUILTINS_DEF): Add sanitizer.def.
>> >>>         * passes.c (init_optimization_passes): Add tsan passes.
>> >>>         * tree-pass.h (register_pass_info): Ditto.
>> >>>         * toplev.c (compile_file): Ditto.
>> >>>         * doc/invoke.texi: Document tsan related options.
>> >>>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
>> >>> if -fsanitize=thread.
>> >>>         * tsan.c: New file about tsan.
>> >>>         * tsan.h: Ditto.
>> >>>         * common.opt: Add -fsanitize=thread.
>> >>>
>> >>> libsanitizer/ChangeLog:
>> >>> 2012-11-22  Wei Mi  <wmi@google.com>
>> >>>
>> >>>         * tsan: New directory. Import tsan runtime from llvm.
>> >>>         * configure.ac: Add 64 bits tsan build.
>> >>>         * Makefile.am: Likewise.
>> >>>         * configure: Regenerated.
>> >>>         * Makefile.in: Likewise.
>> >>>
>> >>> Thanks,
>> >>> Wei.
>> >>>
>> >>> On Sun, Nov 18, 2012 at 10:52 AM, Konstantin Serebryany
>> >>> <konstantin.s.serebryany@gmail.com> wrote:
>> >>> > Just a comment about tsan.
>> >>> > Today, tsan works *only* on x86_64 linux (no 32-bits, no non-linux).
>> >>> > Other 64-bit platforms may be doable, but not as easy as for asan.
>> >>> > Non-linux is harder than non-x86_64 (need to support tons of libc
>> >>> > interceptors).
>> >>> > 32-bit platforms are very hard to port to, I would not bother for
>> >>> > now.
>> >>> > (this probably includes x32, which has cheap atomic 64-bit
>> >>> > loads/stores, but has too small address space for tsan)
>> >>> >
>> >>> > Conclusion: when committing tsan code, please make sure it is enable
>> >>> > only on x86_64
>> >>> >
>> >>> > --kcc
>> >>> >
>> >>> > On Sat, Nov 17, 2012 at 3:13 AM, Wei Mi <wmi@google.com> wrote:
>> >>> >> Hi,
>> >>> >>
>> >>> >> Is it ok for the trunk?
>> >>> >>
>> >>> >> Thanks,
>> >>> >> Wei.
>> >>> >>
>> >>> >> On Tue, Nov 13, 2012 at 5:06 PM, Wei Mi <wmi@google.com> wrote:
>> >>> >>> Thanks for catching this. I update the patch.
>> >>> >>>
>> >>> >>> Regards,
>> >>> >>> Wei.
>> >>> >>>
>> >>> >>> On Tue, Nov 13, 2012 at 4:54 PM, Richard Henderson
>> >>> >>> <rth@redhat.com>
>> >>> >>> wrote:
>> >>> >>>> On 11/13/2012 04:08 PM, Wei Mi wrote:
>> >>> >>>>> +extern void tsan_finish_file (void);
>> >>> >>>>> +
>> >>> >>>>> +#endif /* TREE_TSAN */
>> >>> >>>>> +/* ThreadSanitizer, a data race detector.
>> >>> >>>>> +   Copyright (C) 2011 Free Software Foundation, Inc.
>> >>> >>>>> +   Contributed by Dmitry Vyukov <dvyukov@google.com>
>> >>> >>>>
>> >>> >>>> Careful, you've got double applied patches there.
>> >>> >>>>
>> >>> >>>>
>> >>> >>>> r~
>> >>
>> >>
>> >
>
>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22  9:54                             ` [tsan] ThreadSanitizer instrumentation part Jakub Jelinek
  2012-11-22 13:21                               ` [tsan] libsanitizer tweaks Jakub Jelinek
@ 2012-11-22 22:08                               ` Wei Mi
  2012-11-22 23:04                                 ` H.J. Lu
  2012-11-22 23:18                                 ` Jack Howarth
  1 sibling, 2 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-22 22:08 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Konstantin Serebryany, Richard Henderson, GCC Patches, David Li,
	Diego Novillo, Dodji Seketeli, Dmitry Vyukov

Thanks. I checked in the code.
Committed revision 193736.
Committed revision 193737.

Wei.

On Thu, Nov 22, 2012 at 1:54 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Nov 21, 2012 at 11:22:51PM -0800, Wei Mi wrote:
>> I update the tsan patch against trunk, and create libtsan patch.
>> Please see if it is ok.
>>
>> gcc/ChangeLog:
>> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>>             Wei Mi  <wmi@google.com>
>>
>>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>>         * sanitizer.def: Ditto.
>>         * Makefile.in (tsan.o): Add tsan.o target.
>>         (BUILTINS_DEF): Add sanitizer.def.
>>         * passes.c (init_optimization_passes): Add tsan passes.
>>         * tree-pass.h (register_pass_info): Ditto.
>>         * toplev.c (compile_file): Ditto.
>>         * doc/invoke.texi: Document tsan related options.
>>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
>> if -fsanitize=thread.
>>         * tsan.c: New file about tsan.
>>         * tsan.h: Ditto.
>>         * common.opt: Add -fsanitize=thread.
>>
>> libsanitizer/ChangeLog:
>> 2012-11-22  Wei Mi  <wmi@google.com>
>>
>>         * tsan: New directory. Import tsan runtime from llvm.
>>         * configure.ac: Add 64 bits tsan build.
>>         * Makefile.am: Likewise.
>>         * configure: Regenerated.
>>         * Makefile.in: Likewise.
>
> Ok, thanks.  The comments can be fixed up incrementally.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22 22:08                               ` [tsan] ThreadSanitizer instrumentation part Wei Mi
@ 2012-11-22 23:04                                 ` H.J. Lu
  2012-11-22 23:06                                   ` Jakub Jelinek
  2012-11-22 23:18                                 ` Jack Howarth
  1 sibling, 1 reply; 56+ messages in thread
From: H.J. Lu @ 2012-11-22 23:04 UTC (permalink / raw)
  To: Wei Mi
  Cc: Jakub Jelinek, Konstantin Serebryany, Richard Henderson,
	GCC Patches, David Li, Diego Novillo, Dodji Seketeli,
	Dmitry Vyukov

On Thu, Nov 22, 2012 at 2:08 PM, Wei Mi <wmi@google.com> wrote:
> Thanks. I checked in the code.
> Committed revision 193736.
> Committed revision 193737.
>
> Wei.
>
> On Thu, Nov 22, 2012 at 1:54 AM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Wed, Nov 21, 2012 at 11:22:51PM -0800, Wei Mi wrote:
>>> I update the tsan patch against trunk, and create libtsan patch.
>>> Please see if it is ok.
>>>
>>> gcc/ChangeLog:
>>> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>>>             Wei Mi  <wmi@google.com>
>>>
>>>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>>>         * sanitizer.def: Ditto.
>>>         * Makefile.in (tsan.o): Add tsan.o target.
>>>         (BUILTINS_DEF): Add sanitizer.def.
>>>         * passes.c (init_optimization_passes): Add tsan passes.
>>>         * tree-pass.h (register_pass_info): Ditto.
>>>         * toplev.c (compile_file): Ditto.
>>>         * doc/invoke.texi: Document tsan related options.
>>>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
>>> if -fsanitize=thread.
>>>         * tsan.c: New file about tsan.
>>>         * tsan.h: Ditto.
>>>         * common.opt: Add -fsanitize=thread.
>>>
>>> libsanitizer/ChangeLog:
>>> 2012-11-22  Wei Mi  <wmi@google.com>
>>>
>>>         * tsan: New directory. Import tsan runtime from llvm.
>>>         * configure.ac: Add 64 bits tsan build.
>>>         * Makefile.am: Likewise.
>>>         * configure: Regenerated.
>>>         * Makefile.in: Likewise.
>>
>> Ok, thanks.  The comments can be fixed up incrementally.
>>
>>         Jakub

It failed to bootstrap on Linux/i686:

http://gcc.gnu.org/ml/gcc-regression/2012-11/msg00409.html

It tried to build tsan on Linux/i686.

-- 
H.J.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22 23:04                                 ` H.J. Lu
@ 2012-11-22 23:06                                   ` Jakub Jelinek
  0 siblings, 0 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-22 23:06 UTC (permalink / raw)
  To: H.J. Lu
  Cc: Wei Mi, Konstantin Serebryany, Richard Henderson, GCC Patches,
	David Li, Diego Novillo, Dodji Seketeli, Dmitry Vyukov

On Thu, Nov 22, 2012 at 03:04:23PM -0800, H.J. Lu wrote:
> It failed to bootstrap on Linux/i686:
> 
> http://gcc.gnu.org/ml/gcc-regression/2012-11/msg00409.html
> 
> It tried to build tsan on Linux/i686.

I'd hope http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01866.html ought to
fix that.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22 22:08                               ` [tsan] ThreadSanitizer instrumentation part Wei Mi
  2012-11-22 23:04                                 ` H.J. Lu
@ 2012-11-22 23:18                                 ` Jack Howarth
  2012-11-23  0:53                                   ` Wei Mi
  1 sibling, 1 reply; 56+ messages in thread
From: Jack Howarth @ 2012-11-22 23:18 UTC (permalink / raw)
  To: Wei Mi
  Cc: Jakub Jelinek, Konstantin Serebryany, Richard Henderson,
	GCC Patches, David Li, Diego Novillo, Dodji Seketeli,
	Dmitry Vyukov

On Thu, Nov 22, 2012 at 02:08:07PM -0800, Wei Mi wrote:
> Thanks. I checked in the code.
> Committed revision 193736.
> Committed revision 193737.
> 
> Wei.

Wei,
   Unlike libasan, we seem to have issues building libtsan on darwin using the currently proposed
patch...

http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01817.html

The build fails at...

libtool: compile:  /sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/./gcc/g++ -B/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/./gcc/ -nostdinc++ -nostdinc++ -I/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/include/x86_64-apple-darwin12.2.0 -I/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/include -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/libsupc++ -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/include/backward -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/testsuite/util -L/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/src -L/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/src/.libs -B/sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/bin/ -B/sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/lib/ -isystem /sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/include -isystem /sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/sys-include -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I. -I../../../../gcc-4.8-20121122/libsanitizer/tsan -I ../../../../gcc-4.8-20121122/libsanitizer -I ../../../../gcc-4.8-20121122/libsanitizer/include -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions -g -O2 -MT tsan_interceptors.lo -MD -MP -MF .deps/tsan_interceptors.Tpo -c ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc  -fno-common -DPIC -o .libs/tsan_interceptors.o
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘unsigned int wrap_sleep(unsigned int)’:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:164:27: error: ‘sleep’ was not declared in this scope
   unsigned res = sleep(sec);
                           ^
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘int wrap_usleep(long_t)’:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:171:24: error: ‘usleep’ was not declared in this scope
   int res = usleep(usec);
                        ^
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘int wrap_nanosleep(void*, void*)’:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:178:31: error: ‘nanosleep’ was not declared in this scope
   int res = nanosleep(req, rem);
                               ^
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘void (* wrap_signal(int, sighandler_t))(int)’:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:1270:38: error: ‘sigaction’ was not declared in this scope
   int res = sigaction(sig, &act, &old);
                                      ^
In file included from ../../../../gcc-4.8-20121122/libsanitizer/interception/interception.h:184:0,
                 from ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.h:15,
                 from ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:16:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘void __tsan::InitializeInterceptors()’:
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:1377:18: error: ‘longjmp’ was not declared in this scope
   TSAN_INTERCEPT(longjmp);
                  ^
../../../../gcc-4.8-20121122/libsanitizer/interception/interception_mac.h:41:35: note: in definition of macro ‘OVERRIDE_FUNCTION_MAC’
           (::__interception::uptr)old_func, \
                                   ^
../../../../gcc-4.8-20121122/libsanitizer/interception/interception.h:187:35: note: in expansion of macro ‘INTERCEPT_FUNCTION_MAC’
 # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
                                   ^
../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.h:48:30: note: in expansion of macro ‘INTERCEPT_FUNCTION’
 #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
                              ^
etc. Current llvm svn seems to be broken elsewhere (in compiler-rt/lib/ubsan) so I can't test tsan support there as well.
              Jack

> 
> On Thu, Nov 22, 2012 at 1:54 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Wed, Nov 21, 2012 at 11:22:51PM -0800, Wei Mi wrote:
> >> I update the tsan patch against trunk, and create libtsan patch.
> >> Please see if it is ok.
> >>
> >> gcc/ChangeLog:
> >> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
> >>             Wei Mi  <wmi@google.com>
> >>
> >>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
> >>         * sanitizer.def: Ditto.
> >>         * Makefile.in (tsan.o): Add tsan.o target.
> >>         (BUILTINS_DEF): Add sanitizer.def.
> >>         * passes.c (init_optimization_passes): Add tsan passes.
> >>         * tree-pass.h (register_pass_info): Ditto.
> >>         * toplev.c (compile_file): Ditto.
> >>         * doc/invoke.texi: Document tsan related options.
> >>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
> >> if -fsanitize=thread.
> >>         * tsan.c: New file about tsan.
> >>         * tsan.h: Ditto.
> >>         * common.opt: Add -fsanitize=thread.
> >>
> >> libsanitizer/ChangeLog:
> >> 2012-11-22  Wei Mi  <wmi@google.com>
> >>
> >>         * tsan: New directory. Import tsan runtime from llvm.
> >>         * configure.ac: Add 64 bits tsan build.
> >>         * Makefile.am: Likewise.
> >>         * configure: Regenerated.
> >>         * Makefile.in: Likewise.
> >
> > Ok, thanks.  The comments can be fixed up incrementally.
> >
> >         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] libsanitizer tweaks
  2012-11-22 13:21                               ` [tsan] libsanitizer tweaks Jakub Jelinek
@ 2012-11-22 23:22                                 ` Diego Novillo
  0 siblings, 0 replies; 56+ messages in thread
From: Diego Novillo @ 2012-11-22 23:22 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Wei Mi, Dodji Seketeli, Konstantin Serebryany, GCC Patches,
	David Li, Dmitry Vyukov

On Thu, Nov 22, 2012 at 8:20 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 22, 2012 at 10:54:00AM +0100, Jakub Jelinek wrote:
>> Ok, thanks.  The comments can be fixed up incrementally.
>
> I had closer look at the libsanitizer/tsan configury, and I think I'd prefer
> the configure.tgt script to say if tsan should be built or not.
> Furthermore, GCC doesn't support -Wno-c99-extensions flag for C++, that is a
> clang-ism.
> Ok for trunk (after the tsan patches are committed, on top of them)?
>
> 2012-11-22  Jakub Jelinek  <jakub@redhat.com>
>
>         * tsan/Makefile.am (AM_CXXFLAGS): Remove -Wno-variadic-macros.
>         * Makefile.am (SUBDIRS): Guard tsan addition with TSAN_SUPPORTED
>         automake conditional instead of !MULTISUBDIR32.
>         * configure.tgt: Set TSAN_SUPPORTED=yes for x86_64/i686-linux
>         for 64-bit multilib.
>         * configure.ac: Check for void * size, source in configure.tgt,
>         define TSAN_SUPPORTED conditional instead of MULTILIBDIR32.
>         * configure: Regenerated.
>         * Makefile.in: Regenerated.
>         * tsan/Makefile.in: Regenerated.

This is fine if bootstraps succeeds on i686.


Diego.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-22 23:18                                 ` Jack Howarth
@ 2012-11-23  0:53                                   ` Wei Mi
  0 siblings, 0 replies; 56+ messages in thread
From: Wei Mi @ 2012-11-23  0:53 UTC (permalink / raw)
  To: Jack Howarth
  Cc: Jakub Jelinek, Konstantin Serebryany, Richard Henderson,
	GCC Patches, David Li, Diego Novillo, Dodji Seketeli,
	Dmitry Vyukov

Hi Jack,

Koysta mentioned in a previous mail that tsan is only supported on
x86_64 linux (no 32-bits, no non-linux) for now. tsan building should
be disabled on the platforms other than x86-64-linux. Thanks to Jakub
who will provide another patch including this fix soon.

Thanks,
Wei.

On Thu, Nov 22, 2012 at 3:18 PM, Jack Howarth <howarth@bromo.med.uc.edu> wrote:
> On Thu, Nov 22, 2012 at 02:08:07PM -0800, Wei Mi wrote:
>> Thanks. I checked in the code.
>> Committed revision 193736.
>> Committed revision 193737.
>>
>> Wei.
>
> Wei,
>    Unlike libasan, we seem to have issues building libtsan on darwin using the currently proposed
> patch...
>
> http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01817.html
>
> The build fails at...
>
> libtool: compile:  /sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/./gcc/g++ -B/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/./gcc/ -nostdinc++ -nostdinc++ -I/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/include/x86_64-apple-darwin12.2.0 -I/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/include -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/libsupc++ -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/include/backward -I/sw/src/fink.build/gcc48-4.8.0-1000/gcc-4.8-20121122/libstdc++-v3/testsuite/util -L/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/src -L/sw/src/fink.build/gcc48-4.8.0-1000/darwin_objdir/x86_64-apple-darwin12.2.0/libstdc++-v3/src/.libs -B/sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/bin/ -B/sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/lib/ -isystem /sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/include -isystem /sw/lib/gcc4.8/x86_64-apple-darwin12.2.0/sys-include -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I. -I../../../../gcc-4.8-20121122/libsanitizer/tsan -I ../../../../gcc-4.8-20121122/libsanitizer -I ../../../../gcc-4.8-20121122/libsanitizer/include -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions -g -O2 -MT tsan_interceptors.lo -MD -MP -MF .deps/tsan_interceptors.Tpo -c ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc  -fno-common -DPIC -o .libs/tsan_interceptors.o
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘unsigned int wrap_sleep(unsigned int)’:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:164:27: error: ‘sleep’ was not declared in this scope
>    unsigned res = sleep(sec);
>                            ^
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘int wrap_usleep(long_t)’:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:171:24: error: ‘usleep’ was not declared in this scope
>    int res = usleep(usec);
>                         ^
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘int wrap_nanosleep(void*, void*)’:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:178:31: error: ‘nanosleep’ was not declared in this scope
>    int res = nanosleep(req, rem);
>                                ^
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘void (* wrap_signal(int, sighandler_t))(int)’:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:1270:38: error: ‘sigaction’ was not declared in this scope
>    int res = sigaction(sig, &act, &old);
>                                       ^
> In file included from ../../../../gcc-4.8-20121122/libsanitizer/interception/interception.h:184:0,
>                  from ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.h:15,
>                  from ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:16:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc: In function ‘void __tsan::InitializeInterceptors()’:
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.cc:1377:18: error: ‘longjmp’ was not declared in this scope
>    TSAN_INTERCEPT(longjmp);
>                   ^
> ../../../../gcc-4.8-20121122/libsanitizer/interception/interception_mac.h:41:35: note: in definition of macro ‘OVERRIDE_FUNCTION_MAC’
>            (::__interception::uptr)old_func, \
>                                    ^
> ../../../../gcc-4.8-20121122/libsanitizer/interception/interception.h:187:35: note: in expansion of macro ‘INTERCEPT_FUNCTION_MAC’
>  # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
>                                    ^
> ../../../../gcc-4.8-20121122/libsanitizer/tsan/tsan_interceptors.h:48:30: note: in expansion of macro ‘INTERCEPT_FUNCTION’
>  #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
>                               ^
> etc. Current llvm svn seems to be broken elsewhere (in compiler-rt/lib/ubsan) so I can't test tsan support there as well.
>               Jack
>
>>
>> On Thu, Nov 22, 2012 at 1:54 AM, Jakub Jelinek <jakub@redhat.com> wrote:
>> > On Wed, Nov 21, 2012 at 11:22:51PM -0800, Wei Mi wrote:
>> >> I update the tsan patch against trunk, and create libtsan patch.
>> >> Please see if it is ok.
>> >>
>> >> gcc/ChangeLog:
>> >> 2012-11-22  Dmitry Vyukov  <dvyukov@google.com>
>> >>             Wei Mi  <wmi@google.com>
>> >>
>> >>         * builtins.def (DEF_SANITIZER_BUILTIN): Define tsan builtins.
>> >>         * sanitizer.def: Ditto.
>> >>         * Makefile.in (tsan.o): Add tsan.o target.
>> >>         (BUILTINS_DEF): Add sanitizer.def.
>> >>         * passes.c (init_optimization_passes): Add tsan passes.
>> >>         * tree-pass.h (register_pass_info): Ditto.
>> >>         * toplev.c (compile_file): Ditto.
>> >>         * doc/invoke.texi: Document tsan related options.
>> >>         * gcc.c (LINK_COMMAND_SPEC): Add LIBTSAN_SPEC in link command
>> >> if -fsanitize=thread.
>> >>         * tsan.c: New file about tsan.
>> >>         * tsan.h: Ditto.
>> >>         * common.opt: Add -fsanitize=thread.
>> >>
>> >> libsanitizer/ChangeLog:
>> >> 2012-11-22  Wei Mi  <wmi@google.com>
>> >>
>> >>         * tsan: New directory. Import tsan runtime from llvm.
>> >>         * configure.ac: Add 64 bits tsan build.
>> >>         * Makefile.am: Likewise.
>> >>         * configure: Regenerated.
>> >>         * Makefile.in: Likewise.
>> >
>> > Ok, thanks.  The comments can be fixed up incrementally.
>> >
>> >         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH] Convert asan to use sanitizer.def builtins, initialize them if the FE didn't
  2012-11-22 15:13                                 ` [PATCH] Convert asan to use sanitizer.def builtins, initialize them if the FE didn't Jakub Jelinek
@ 2012-12-03 16:58                                   ` Dodji Seketeli
  0 siblings, 0 replies; 56+ messages in thread
From: Dodji Seketeli @ 2012-12-03 16:58 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Dodji Seketeli, Wei Mi, Konstantin Serebryany,
	GCC Patches, David Li, Diego Novillo

Jakub Jelinek <jakub@redhat.com> writes:

> Ok for trunk (the patch is on top of the tsan patch)?
>
> 2012-11-22  Jakub Jelinek  <jakub@redhat.com>
>
> 	* sanitizer.def: Add Address Sanitizer builtins.
> 	Rename BUILT_IN_TSAN_READ_* to BUILT_IN_TSAN_READ* and
> 	BUILT_IN_TSAN_WRITE_* to BUILT_IN_TSAN_WRITE*.
> 	* Makefile.in (asan.o): Depend on langhooks.h.
> 	(tsan.o): Depend on asan.h.
> 	* asan.h (initialize_sanitizer_builtins): New prototype.
> 	* asan.c: Include langhooks.h.
> 	(report_error_func): Use builtin_decl_implicit of corresponding
> 	BUILT_IN_ASAN_REPORT_{LOAD,STORE}*.
> 	(asan_init_func): Removed.
> 	(initialize_sanitizer_builtins): New function.
> 	(asan_finish_file): Call it.  Use builtin_decl_implicit
> 	on BUILT_IN_ASAN_{INIT,{,UN}REGISTER_GLOBALS}.
> 	(asan_instrument): Call initialize_sanitizer_builtins.
> 	* builtins.def (DEF_SANITIZER_BUILTIN): Change condition to
> 	(flag_asan || flag_tsan).
> 	* tsan.c: Include asan.h and tsan.h.
> 	(get_memory_access_decl): Rename BUILT_IN_TSAN_{READ,WRITE}_*
> 	to BUILT_IN_TSAN_{READ,WRITE}*.
> 	(tsan_pass): Call initialize_sanitizer_builtins.
> 	(tsan_gate, tsan_gate_O0): Don't check if
> 	builtin_decl_implicit_p (BUILT_IN_TSAN_INIT) is true.
> 	(tsan_finish_file): Call initialize_sanitizer_builtins.
> 	* builtin-types.def (BT_FN_VOID_PTR_PTRMODE): New fn type.
>
> --- gcc/sanitizer.def.jj	2012-11-22 13:17:24.000000000 +0100
> +++ gcc/sanitizer.def	2012-11-22 15:45:55.873655417 +0100
> @@ -1,3 +1,34 @@
> +/* Address Sanitizer */

Maybe we could use some introductory comment at the beginning of this
file, a bit like what we have for sync-builtins.def.  I think this kind
of comments are a great asset for people who are new to the file.

Otherwise, the asan parts of the patch looks OK to me.

Thanks.

-- 
		Dodji

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-02 15:58     ` Jakub Jelinek
@ 2012-11-02 16:14       ` Xinliang David Li
  0 siblings, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-02 16:14 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

Yes, agree.  For now using the !(TREE_ADDRESSABLE (decl)  ||
is_global_var (decl)) can be used to skip instrumentation if the
points-to interface is not available for use.

David

On Fri, Nov 2, 2012 at 8:58 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Nov 02, 2012 at 08:54:42AM -0700, Xinliang David Li wrote:
>> Thanks, good to know that.
>
> But note that such vars, even when accessed only partially, don't
> need to be tracked by tsan just because of that.
> TREE_ADDRESSABLE (decl) || is_global_var (decl) is really a conservative
> check for what needs to be tracked, and perhaps less conservative one using
> points-to.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-02 15:54   ` Xinliang David Li
@ 2012-11-02 15:58     ` Jakub Jelinek
  2012-11-02 16:14       ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-02 15:58 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: GCC Patches

On Fri, Nov 02, 2012 at 08:54:42AM -0700, Xinliang David Li wrote:
> Thanks, good to know that.

But note that such vars, even when accessed only partially, don't
need to be tracked by tsan just because of that.
TREE_ADDRESSABLE (decl) || is_global_var (decl) is really a conservative
check for what needs to be tracked, and perhaps less conservative one using
points-to.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-02 13:31 ` Martin Jambor
@ 2012-11-02 15:54   ` Xinliang David Li
  2012-11-02 15:58     ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-02 15:54 UTC (permalink / raw)
  To: Xinliang David Li, GCC Patches

Thanks, good to know that.

David

On Fri, Nov 2, 2012 at 6:31 AM, Martin Jambor <mjambor@suse.cz> wrote:
> Hi,
>
> On Thu, Nov 01, 2012 at 11:11:13AM -0700, Xinliang David Li wrote:
>>
>
> ...
>
>> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
>> of ssa name) appears in the assignment, why would it not be
>> 'addressable'?
>
> There are other reason beside being TREE_ADDRESSABLE that may cause a
> "gimple scalar" type variable not to be in SSA form.  Usually this
> means that there is a write access to only a part of it.  This happens
> often with vector or complex types which are deemed scalar types by
> gimple but are obviously quite special.  Such decls have their
> DECL_GIMPLE_REG_P cleared.
>
> Martin

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:11 Xinliang David Li
                   ` (2 preceding siblings ...)
  2012-11-01 18:24 ` Jakub Jelinek
@ 2012-11-02 13:31 ` Martin Jambor
  2012-11-02 15:54   ` Xinliang David Li
  3 siblings, 1 reply; 56+ messages in thread
From: Martin Jambor @ 2012-11-02 13:31 UTC (permalink / raw)
  To: Xinliang David Li; +Cc: GCC Patches

Hi,

On Thu, Nov 01, 2012 at 11:11:13AM -0700, Xinliang David Li wrote:
> 

...

> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
> of ssa name) appears in the assignment, why would it not be
> 'addressable'? 

There are other reason beside being TREE_ADDRESSABLE that may cause a
"gimple scalar" type variable not to be in SSA form.  Usually this
means that there is a write access to only a part of it.  This happens
often with vector or complex types which are deemed scalar types by
gimple but are obviously quite special.  Such decls have their
DECL_GIMPLE_REG_P cleared.

Martin

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 21:24         ` Jakub Jelinek
@ 2012-11-01 21:43           ` Xinliang David Li
  0 siblings, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 21:43 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

It is not always true though -- only when the array address is picked
by the pass to be the induction variable.

David

On Thu, Nov 1, 2012 at 2:24 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 02:19:43PM -0700, Xinliang David Li wrote:
>> Right -- I found the same thing, it is the ivopt that resets it -- the
>> IV opt pass should have a TODO_update_address_taken.
>
> That wouldn't change anything.  The IVopts pass adds some_ptr = &a[0];
> to the IL, so a is then address taken.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 21:19       ` Xinliang David Li
@ 2012-11-01 21:24         ` Jakub Jelinek
  2012-11-01 21:43           ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 21:24 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 02:19:43PM -0700, Xinliang David Li wrote:
> Right -- I found the same thing, it is the ivopt that resets it -- the
> IV opt pass should have a TODO_update_address_taken.

That wouldn't change anything.  The IVopts pass adds some_ptr = &a[0];
to the IL, so a is then address taken.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 21:07     ` Jakub Jelinek
@ 2012-11-01 21:19       ` Xinliang David Li
  2012-11-01 21:24         ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 21:19 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

Right -- I found the same thing, it is the ivopt that resets it -- the
IV opt pass should have a TODO_update_address_taken.

thanks,

David

On Thu, Nov 1, 2012 at 2:07 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 01:57:40PM -0700, Xinliang David Li wrote:
>> I looked at it pretty late in the pipeline -- during cfgexpand and I
>> had specified -O2 in the command line.
>
> That was too late, ivopts pass (after pre where tsan pass was put around)
> marks it TREE_ADDRESSABLE again, because it creates IV with initial
> value &a[0].
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 20:57   ` Xinliang David Li
@ 2012-11-01 21:07     ` Jakub Jelinek
  2012-11-01 21:19       ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 21:07 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 01:57:40PM -0700, Xinliang David Li wrote:
> I looked at it pretty late in the pipeline -- during cfgexpand and I
> had specified -O2 in the command line.

That was too late, ivopts pass (after pre where tsan pass was put around)
marks it TREE_ADDRESSABLE again, because it creates IV with initial
value &a[0].

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 20:54 ` Jakub Jelinek
@ 2012-11-01 20:57   ` Xinliang David Li
  2012-11-01 21:07     ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 20:57 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 1, 2012 at 1:47 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 12:34:54PM -0700, Xinliang David Li wrote:
>> On Thu, Nov 1, 2012 at 12:16 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> > On Thu, Nov 01, 2012 at 11:57:21AM -0700, Xinliang David Li wrote:
>> >> That is exactly related to tsan -- it should skip local variable that
>> >> is not address taken (though still addressable, which by addressable,
>> >> it means the variable has a memory home).
>> >>
>> >> The problem is that if 'addressable' bit is not equivalent to 'address
>> >> taken', it will be too conservative to use it --- as addressable
>> >> variables may not be address taken.
>> >
>> > But TREE_ADDRESSABLE is equivalent to address taken.
>>
>> It seems not -- unless our definition of them is different. I debugged
>> the following program:
>>
>>
>> int foo()
>> {
>>   int a[1000];
>>   int i, s;
>>
>>   for (i = 0; i < 1000; i++)
>>     a[i] = i;
>>
>>   for (i = 0; i < 1000; i++)
>>     s += a[i];
>>
>>    return s;
>> }
>>
>> a's address is not taken (at source level), but it is marked as addressable.
>
> The question is when you are looking at TREE_ADDRESSABLE.  Sure, the FE
> marks it as TREE_ADDRESSABLE conservatively, but if you compile with -O2,
> in execute_update_addresses_taken called after the ccp pass TREE_ADDRESSABLE
> is cleared.  I thought the tsan pass was scheduled much later than that
> (around PRE pass).

I looked at it pretty late in the pipeline -- during cfgexpand and I
had specified -O2 in the command line.

>
>> > If you need it even less conservative, I guess you could do say:
>> >   struct ptr_info_def pi;
>> >   memset (&pi, 0, sizeof (pi));
>> >   pi.escaped = 1;
>> >   pi.nonlocal = 1;
>> >   return pt_solution_includes (&pi, decl);
>> >
>>
>> That will be nice.  Are points-to info exposed to client code like
>> this? Are there standard aliaser interfaces for them?
>
> pt_solution_includes is exported interface, though we'd need to ask richi
> when he returns whether he is fine with such an use.

yes, It is better to wrap it up in a simpler interface.

David


>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 19:35 Xinliang David Li
@ 2012-11-01 20:54 ` Jakub Jelinek
  2012-11-01 20:57   ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 20:54 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 12:34:54PM -0700, Xinliang David Li wrote:
> On Thu, Nov 1, 2012 at 12:16 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Thu, Nov 01, 2012 at 11:57:21AM -0700, Xinliang David Li wrote:
> >> That is exactly related to tsan -- it should skip local variable that
> >> is not address taken (though still addressable, which by addressable,
> >> it means the variable has a memory home).
> >>
> >> The problem is that if 'addressable' bit is not equivalent to 'address
> >> taken', it will be too conservative to use it --- as addressable
> >> variables may not be address taken.
> >
> > But TREE_ADDRESSABLE is equivalent to address taken.
> 
> It seems not -- unless our definition of them is different. I debugged
> the following program:
> 
> 
> int foo()
> {
>   int a[1000];
>   int i, s;
> 
>   for (i = 0; i < 1000; i++)
>     a[i] = i;
> 
>   for (i = 0; i < 1000; i++)
>     s += a[i];
> 
>    return s;
> }
> 
> a's address is not taken (at source level), but it is marked as addressable.

The question is when you are looking at TREE_ADDRESSABLE.  Sure, the FE
marks it as TREE_ADDRESSABLE conservatively, but if you compile with -O2,
in execute_update_addresses_taken called after the ccp pass TREE_ADDRESSABLE
is cleared.  I thought the tsan pass was scheduled much later than that
(around PRE pass).

> > If you need it even less conservative, I guess you could do say:
> >   struct ptr_info_def pi;
> >   memset (&pi, 0, sizeof (pi));
> >   pi.escaped = 1;
> >   pi.nonlocal = 1;
> >   return pt_solution_includes (&pi, decl);
> >
> 
> That will be nice.  Are points-to info exposed to client code like
> this? Are there standard aliaser interfaces for them?

pt_solution_includes is exported interface, though we'd need to ask richi
when he returns whether he is fine with such an use.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]           ` <CACT4Y+bbG3MOBX0vmP-Dvf=oPDp6GBDodJ+FbHrXEmjHjWn-qw@mail.gmail.com>
@ 2012-11-01 20:53             ` Jakub Jelinek
  0 siblings, 0 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 20:53 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Xinliang David Li, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 11:35:42PM +0400, Dmitry Vyukov wrote:
> Wow! It's cool!
> Is it costly to compute?

No, it is just a couple of instructions and bitmap_bit_p test.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
@ 2012-11-01 19:35 Xinliang David Li
  2012-11-01 20:54 ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 19:35 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 1, 2012 at 12:16 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 11:57:21AM -0700, Xinliang David Li wrote:
>> That is exactly related to tsan -- it should skip local variable that
>> is not address taken (though still addressable, which by addressable,
>> it means the variable has a memory home).
>>
>> The problem is that if 'addressable' bit is not equivalent to 'address
>> taken', it will be too conservative to use it --- as addressable
>> variables may not be address taken.
>
> But TREE_ADDRESSABLE is equivalent to address taken.

It seems not -- unless our definition of them is different. I debugged
the following program:


int foo()
{
  int a[1000];
  int i, s;

  for (i = 0; i < 1000; i++)
    a[i] = i;

  for (i = 0; i < 1000; i++)
    s += a[i];

   return s;
}

a's address is not taken (at source level), but it is marked as addressable.

>
>> Note that even 'address taken' is too conservative to use here. Only
>> when it is escaped the thread (via non TLS global pointers or other
>> escaped local, heap memories), should it be considered to be
>> instrumented.
>
> If you need it even less conservative, I guess you could do say:
>   struct ptr_info_def pi;
>   memset (&pi, 0, sizeof (pi));
>   pi.escaped = 1;
>   pi.nonlocal = 1;
>   return pt_solution_includes (&pi, decl);
>

That will be nice.  Are points-to info exposed to client code like
this? Are there standard aliaser interfaces for them?

thanks,

David

> (the nonlocal in pt_solution_includes_1 performs the is_global_var check
> that needs to be done, and escaped checks whether decl is in the escaped
> set).
>
> Then say for
> int foo(int i)
> {
>   int a[100], b[100], c[100], d[100], *p;
>   if (i < 10)
>     p = a;
>   else if (i < 60)
>     p = b;
>   else if (i <= 65)
>     p = c;
>   else
>     p = d;
>   p[i] = 1;
>   p[2 * i] = 2;
>   return p[i + 1];
> }
> still none of a, b, c, and d vars will be included, even when they are
> TREE_ADDRESSABLE, but if you say pass p to another function, or set a global
> var to it, it will already return true for them.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:57       ` Xinliang David Li
@ 2012-11-01 19:16         ` Jakub Jelinek
       [not found]           ` <CACT4Y+bbG3MOBX0vmP-Dvf=oPDp6GBDodJ+FbHrXEmjHjWn-qw@mail.gmail.com>
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 19:16 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 11:57:21AM -0700, Xinliang David Li wrote:
> That is exactly related to tsan -- it should skip local variable that
> is not address taken (though still addressable, which by addressable,
> it means the variable has a memory home).
> 
> The problem is that if 'addressable' bit is not equivalent to 'address
> taken', it will be too conservative to use it --- as addressable
> variables may not be address taken.

But TREE_ADDRESSABLE is equivalent to address taken.

> Note that even 'address taken' is too conservative to use here. Only
> when it is escaped the thread (via non TLS global pointers or other
> escaped local, heap memories), should it be considered to be
> instrumented.

If you need it even less conservative, I guess you could do say:
  struct ptr_info_def pi;
  memset (&pi, 0, sizeof (pi));
  pi.escaped = 1;
  pi.nonlocal = 1;
  return pt_solution_includes (&pi, decl);

(the nonlocal in pt_solution_includes_1 performs the is_global_var check
that needs to be done, and escaped checks whether decl is in the escaped
set).

Then say for
int foo(int i)
{
  int a[100], b[100], c[100], d[100], *p;
  if (i < 10)
    p = a;
  else if (i < 60)
    p = b;
  else if (i <= 65)
    p = c;
  else
    p = d;
  p[i] = 1;
  p[2 * i] = 2;
  return p[i + 1];
}
still none of a, b, c, and d vars will be included, even when they are
TREE_ADDRESSABLE, but if you say pass p to another function, or set a global
var to it, it will already return true for them.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:49     ` Jakub Jelinek
@ 2012-11-01 18:57       ` Xinliang David Li
  2012-11-01 19:16         ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 18:57 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

That is exactly related to tsan -- it should skip local variable that
is not address taken (though still addressable, which by addressable,
it means the variable has a memory home).

The problem is that if 'addressable' bit is not equivalent to 'address
taken', it will be too conservative to use it --- as addressable
variables may not be address taken.

Note that even 'address taken' is too conservative to use here. Only
when it is escaped the thread (via non TLS global pointers or other
escaped local, heap memories), should it be considered to be
instrumented.

David


On Thu, Nov 1, 2012 at 11:49 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 11:32:01AM -0700, Xinliang David Li wrote:
>> For the following case:
>>
>> int foo()
>> {
>>    int a[100];
>>
>>     // use 'a' as a[i]
>>     ...
>> }
>>
>> Assuming there is no assignment of the form p = &a[i] in the source,
>> variable 'a' still will be allocated in stack and its address is is
>> needed. Is the addressable bit set for 'a'?   If yes, then it is not
>> the same as address taken.
>
> But how is that relevant to tsan?  If TREE_ADDRESSABLE isn't set on a,
> then yes, you can still use a[i] = 6; or return a[j]; on it, and it will
> be allocated on stack, but different threads can't access the array, so
> there is no data race possible.
> Only if a is static int a[100]; above, or otherwise is_global_var (decl).
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:32   ` Xinliang David Li
@ 2012-11-01 18:49     ` Jakub Jelinek
  2012-11-01 18:57       ` Xinliang David Li
  0 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 18:49 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 11:32:01AM -0700, Xinliang David Li wrote:
> For the following case:
> 
> int foo()
> {
>    int a[100];
> 
>     // use 'a' as a[i]
>     ...
> }
> 
> Assuming there is no assignment of the form p = &a[i] in the source,
> variable 'a' still will be allocated in stack and its address is is
> needed. Is the addressable bit set for 'a'?   If yes, then it is not
> the same as address taken.

But how is that relevant to tsan?  If TREE_ADDRESSABLE isn't set on a,
then yes, you can still use a[i] = 6; or return a[j]; on it, and it will
be allocated on stack, but different threads can't access the array, so
there is no data race possible.
Only if a is static int a[100]; above, or otherwise is_global_var (decl).

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:24 ` Jakub Jelinek
@ 2012-11-01 18:32   ` Xinliang David Li
  2012-11-01 18:49     ` Jakub Jelinek
  0 siblings, 1 reply; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 18:32 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

For the following case:

int foo()
{
   int a[100];

    // use 'a' as a[i]
    ...
}

Assuming there is no assignment of the form p = &a[i] in the source,
variable 'a' still will be allocated in stack and its address is is
needed. Is the addressable bit set for 'a'?   If yes, then it is not
the same as address taken.

David


On Thu, Nov 1, 2012 at 11:24 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Nov 01, 2012 at 11:11:13AM -0700, Xinliang David Li wrote:
>> But it skips those globals without static storage and marked as not addressable.
>>
>> It seems to me you want to skip all stack local variables that are not
>> address escaped.  Without address escape analysis, the address taken
>> bit (not the same as addressable attribute) should be used. As far as
>> I can tell, such bit is not available in var_decl. The varpool_node
>> has one, but it is only for var_decls with static storage.  It is also
>> unfortunate that there is no single bit to test if a variable is
>> function auto, though there is an interface to call which is
>> 'auto_var_in_fn_p (...)'.  The condition to skip such variable
>> references are:
>>
>>   if (tcode == VAR_DECL && auto_var_in_fn_p (expr, ..) &&
>> !address_taken (expr))
>>
>> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
>> of ssa name) appears in the assignment, why would it not be
>> 'addressable'? And being addressable does not mean it is address taken
>> either.
>
> TREE_ADDRESSABLE really is a flag whether the
> address of the decl is ever taken (or addresses of its fields etc.),
> and it is updated from time to time (execute_update_addresses_taken)
> after certain passes.  Don't understand why you think it is redundant.
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:11 Xinliang David Li
       [not found] ` <CACT4Y+azNYV+8ieRthd-SkQqGO82dj2+JOdL5c15DyBc9LwyfA@mail.gmail.com>
  2012-11-01 18:22 ` Diego Novillo
@ 2012-11-01 18:24 ` Jakub Jelinek
  2012-11-01 18:32   ` Xinliang David Li
  2012-11-02 13:31 ` Martin Jambor
  3 siblings, 1 reply; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01 18:24 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 11:11:13AM -0700, Xinliang David Li wrote:
> But it skips those globals without static storage and marked as not addressable.
> 
> It seems to me you want to skip all stack local variables that are not
> address escaped.  Without address escape analysis, the address taken
> bit (not the same as addressable attribute) should be used. As far as
> I can tell, such bit is not available in var_decl. The varpool_node
> has one, but it is only for var_decls with static storage.  It is also
> unfortunate that there is no single bit to test if a variable is
> function auto, though there is an interface to call which is
> 'auto_var_in_fn_p (...)'.  The condition to skip such variable
> references are:
> 
>   if (tcode == VAR_DECL && auto_var_in_fn_p (expr, ..) &&
> !address_taken (expr))
> 
> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
> of ssa name) appears in the assignment, why would it not be
> 'addressable'? And being addressable does not mean it is address taken
> either.

TREE_ADDRESSABLE really is a flag whether the
address of the decl is ever taken (or addresses of its fields etc.),
and it is updated from time to time (execute_update_addresses_taken)
after certain passes.  Don't understand why you think it is redundant.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-11-01 18:11 Xinliang David Li
       [not found] ` <CACT4Y+azNYV+8ieRthd-SkQqGO82dj2+JOdL5c15DyBc9LwyfA@mail.gmail.com>
@ 2012-11-01 18:22 ` Diego Novillo
  2012-11-01 18:24 ` Jakub Jelinek
  2012-11-02 13:31 ` Martin Jambor
  3 siblings, 0 replies; 56+ messages in thread
From: Diego Novillo @ 2012-11-01 18:22 UTC (permalink / raw)
  To: Xinliang David Li
  Cc: Dmitry Vyukov, Jakub Jelinek, Wei Mi, GCC Patches, Dodji Seketeli

On Thu, Nov 1, 2012 at 2:11 PM, Xinliang David Li <davidxl@google.com> wrote:

> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
> of ssa name) appears in the assignment, why would it not be
> 'addressable'? And being addressable does not mean it is address taken
> either.

Yes, it does mean that.

> Diego, is there a way to check address taken attribute for auto vars?

TREE_ADDRESSABLE is it.  If that bit is set, the address of the
variable has been taken.


Diego.

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]   ` <CACT4Y+YQDBVxL2GdK9o-8kn===1XzX9JaBJUpM5N4MYCJAeo=Q@mail.gmail.com>
@ 2012-11-01 18:20     ` Xinliang David Li
  0 siblings, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 18:20 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Jakub Jelinek, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 1, 2012 at 11:16 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Nov 1, 2012 at 10:14 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>>>
>>> >> > On Wed, Oct 31, 2012 at 11:00:17PM -0700, Xinliang David Li wrote:
>>> >> >> > +  /* 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_ADDRESSABLE (expr)
>>> >> >>
>>> >> >> > +         && TREE_STATIC (expr) == 0)
>>> >> >>
>>> >> >>
>>> >> >> To detect stack varaible, TREE_STATIC can not be used.  Use
>>> >> >> !DECL_EXTERNAL instead.
>>> >> >
>>> >> > TREE_STATIC is right for that.
>>> >>
>>> >> Hmm, what does this condition try to capture then?
>>> >
>>> >
>>> >
>>> > As far as I remember, I've seen global variables marked as
>>> > !ADDRESSABLE. And
>>> > this condition is to instrument global vars in any case.
>>> > But it was a long time ago.
>>>
>>> But it skips those globals without static storage and marked as not
>>> addressable.
>>>
>>> It seems to me you want to skip all stack local variables that are not
>>> address escaped.
>>
>>
>> Yes, that's correct. I am pretty sure I implemented it incorrectly :)
>
>
>
> The high-level intention is to skip memory accesses that can't constitute a
> data race.
> Originally there were some other checks, e.g. check for loads from vtable.
>

That should be caught by the is_load_const check there.

David


>
>
>>
>>
>>>
>>> Without address escape analysis, the address taken
>>> bit (not the same as addressable attribute) should be used. As far as
>>> I can tell, such bit is not available in var_decl. The varpool_node
>>> has one, but it is only for var_decls with static storage.  It is also
>>> unfortunate that there is no single bit to test if a variable is
>>> function auto, though there is an interface to call which is
>>> 'auto_var_in_fn_p (...)'.  The condition to skip such variable
>>> references are:
>>>
>>>   if (tcode == VAR_DECL && auto_var_in_fn_p (expr, ..) &&
>>> !address_taken (expr))
>>>
>>> The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
>>> of ssa name) appears in the assignment, why would it not be
>>> 'addressable'? And being addressable does not mean it is address taken
>>> either.
>>>
>>> Diego, is there a way to check address taken attribute for auto vars?
>>>
>>> thanks,
>>>
>>> David
>>
>>
>

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
@ 2012-11-01 18:11 Xinliang David Li
       [not found] ` <CACT4Y+azNYV+8ieRthd-SkQqGO82dj2+JOdL5c15DyBc9LwyfA@mail.gmail.com>
                   ` (3 more replies)
  0 siblings, 4 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01 18:11 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Jakub Jelinek, Wei Mi, GCC Patches, Diego Novillo, Dodji Seketeli

On Thu, Nov 1, 2012 at 10:06 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Nov 1, 2012 at 7:47 PM, Xinliang David Li <davidxl@google.com>
> wrote:
>>
>> On Wed, Oct 31, 2012 at 11:27 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> > On Wed, Oct 31, 2012 at 11:00:17PM -0700, Xinliang David Li wrote:
>> >> > +  /* 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_ADDRESSABLE (expr)
>> >>
>> >> > +         && TREE_STATIC (expr) == 0)
>> >>
>> >>
>> >> To detect stack varaible, TREE_STATIC can not be used.  Use
>> >> !DECL_EXTERNAL instead.
>> >
>> > TREE_STATIC is right for that.
>>
>> Hmm, what does this condition try to capture then?
>
>
>
> As far as I remember, I've seen global variables marked as !ADDRESSABLE. And
> this condition is to instrument global vars in any case.
> But it was a long time ago.

But it skips those globals without static storage and marked as not addressable.

It seems to me you want to skip all stack local variables that are not
address escaped.  Without address escape analysis, the address taken
bit (not the same as addressable attribute) should be used. As far as
I can tell, such bit is not available in var_decl. The varpool_node
has one, but it is only for var_decls with static storage.  It is also
unfortunate that there is no single bit to test if a variable is
function auto, though there is an interface to call which is
'auto_var_in_fn_p (...)'.  The condition to skip such variable
references are:

  if (tcode == VAR_DECL && auto_var_in_fn_p (expr, ..) &&
!address_taken (expr))

The TREE_ADDRESSABLE check seems redundant -- if the var_decl (instead
of ssa name) appears in the assignment, why would it not be
'addressable'? And being addressable does not mean it is address taken
either.

Diego, is there a way to check address taken attribute for auto vars?

thanks,

David

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
       [not found]   ` <CACT4Y+Z77tgge+k4+DOMkCLOWL4-xhate5Ccmav3Vox3_8Pqcw@mail.gmail.com>
@ 2012-11-01  7:22     ` Jakub Jelinek
  0 siblings, 0 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01  7:22 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Wei Mi, gcc-patches, David Li, Diego Novillo, Dodji Seketeli

On Thu, Nov 01, 2012 at 10:01:31AM +0400, Dmitry Vyukov wrote:
> > For gimplification context, see above, would be better to just
> > emit gimple directly.
> > For func_calls and func_mops, I believe why you need two variables instead
> > of just one, and why the function can't just return a bool whether
> > entry/exit needs to be instrumented or not.
> >
> 
> It was useful for debugging and stats collection.
> We currently have something similar in llvm passes as well. E.g. when you
> add an additional optimization that eliminates some instrumentation, you
> need to see how much it helps.

But you are not using it anywhere but to decide if entry/exit should be
instrumented.  Either you should put some message into the pass dump
if -fdump-tree-tsan{,0}, then you can just use that option and grep,
or for one time statistics gathering I think people are usually using
one-off hacks, some statistics variables, and fopen ("/tmp/somefile", "a");
fprintf (, ...); them either before exit or upon each change, then
gather that across say bootstrap/regtest and build of some interesting
packages.  But such hacks shouldn't remain in gcc sources.
For how many __tsan_* calls have been emitted IMHO the easiest is just
to objdump -dr the resulting binary or shared library.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-10-31 18:56 Wei Mi
  2012-10-31 23:17 ` Jakub Jelinek
@ 2012-11-01  6:58 ` Jakub Jelinek
  1 sibling, 0 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-11-01  6:58 UTC (permalink / raw)
  To: Wei Mi
  Cc: gcc-patches, Dmitry Vyukov, David Li, Diego Novillo, Dodji Seketeli

On Wed, Oct 31, 2012 at 11:34:10AM -0700, Wei Mi wrote:
+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;
+}

The above can crash the compiler btw, as that static tree decl
(in many other functions) is not GTY(()) marked (must be file scope for
that), thus ggc_collect might free it.  Also, please use type
instead of typ for variable names.

> +  /* 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);

Inserting stmts after a call may or may not work.  E.g. if the call
can throw, it must be the last stmt in a basic block, so then the
stmts need to be inserted on a successor edge.  Similarly noreturn
call must be last (but in that case it shouldn't have lhs).

> +  gcode = gimple_code (stmt);
> +  if (gcode == GIMPLE_CALL)

is_gimple_call (stmt)

> +    {
> +      if (gimple_call_fndecl (stmt) != get_init_decl ())
> +        func_calls++;
> +    }
> +  else if (gcode == GIMPLE_ASSIGN)

is_gimple_assign (stmt)

> +    {
> +      /* Handle assignment lhs as store.  */
> +      lhs = gimple_assign_lhs (stmt);
> +      instrument_expr (gsi, lhs, 1);

To find what a store or load is, you can just use the new
gimple_store_p (stmt) and gimple_assign_load_p (stmt)
predicates, or at least just do gimple_assign_single_p (stmt)
to guard instrument_expr calls on both lhs and rhs1.
No need to scan all operands, only single rhs assignments
can be loads.  And as David said, use true/false instead of 1/0.

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-10-31 23:17 ` Jakub Jelinek
@ 2012-11-01  6:02   ` Xinliang David Li
       [not found]   ` <CACT4Y+Z77tgge+k4+DOMkCLOWL4-xhate5Ccmav3Vox3_8Pqcw@mail.gmail.com>
  1 sibling, 0 replies; 56+ messages in thread
From: Xinliang David Li @ 2012-11-01  6:02 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Wei Mi, GCC Patches, Dmitry Vyukov, Diego Novillo, Dodji Seketeli

On Wed, Oct 31, 2012 at 4:10 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> Hi!
>
> Just a couple of random comments:
>
> On Wed, Oct 31, 2012 at 11:34:10AM -0700, Wei Mi wrote:
>> gcc/ChangeLog:
>> 2012-10-31  Wei Mi  <wmi@gmail.com>
>
> If Dmitry wrote parts of the patch, it would be nice to mention
> him in the ChangeLog too.
>
>>         * 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
>
> All ChangeLog entries should end with a dot.
>
>> --- 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
>>  <http://www.gnu.org/licenses/>.  */
>>
>> +#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 */
>
> Why this?  Simply don't include that header in tsan.c, it is already
> included by basic-block.h.
>
>> --- 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/
>> + */
>
> Can't google just assign the code to FSF, and use a standard boilerplate
> as everything else in gcc/ ?
>
>> +/* 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;
>> +}
> ...
>
> Instead of this (but same applies to asan), I think we should just consider
> putting it into builtins.def (or have sanitizer.def like there is sync.def
> or omp-builtins.def).  The problem might be non-C/C++ family frontends
> though.
>


This sounds good -- may be better to defer this and combine the effort
with asan.


David




>> +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;
>
> int_size_in_bytes.
>
>> +  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;
>
> I think it is weird to return gimple_seqs, it would be better to just
> emit the code at some stmt iterator, and preferrably without building
> everything as trees, then gimplifying it.
>> +}
>> +
>> +/* 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)
>
> This really should use a stmt iterator.
>
>> +  FOR_EACH_BB (bb)
>> +    {
>> +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>> +        {
>> +          instrument_gimple (gsi);
>> +        }
>> +    }
>
> Extraneous two pairs of {}s.
>
>> +  struct gimplify_ctx gctx;
>> +
>> +  func_calls = 0;
>> +  func_mops = 0;
>
> For gimplification context, see above, would be better to just
> emit gimple directly.
> For func_calls and func_mops, I believe why you need two variables instead
> of just one, and why the function can't just return a bool whether
> entry/exit needs to be instrumented or not.
>> +  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 = {{
>
> Please watch formatting of other gimple_opt_pass structures.
> {{ isn't used anywhere.
>
>> --- 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
>
> Is that the option that LLVM uses (I'm talking about -faddress-sanitizer
> in LLVM vs. -fasan right now in GCC, isn't that similar?).
>
>         Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [tsan] ThreadSanitizer instrumentation part
  2012-10-31 18:56 Wei Mi
@ 2012-10-31 23:17 ` Jakub Jelinek
  2012-11-01  6:02   ` Xinliang David Li
       [not found]   ` <CACT4Y+Z77tgge+k4+DOMkCLOWL4-xhate5Ccmav3Vox3_8Pqcw@mail.gmail.com>
  2012-11-01  6:58 ` Jakub Jelinek
  1 sibling, 2 replies; 56+ messages in thread
From: Jakub Jelinek @ 2012-10-31 23:17 UTC (permalink / raw)
  To: Wei Mi
  Cc: gcc-patches, Dmitry Vyukov, David Li, Diego Novillo, Dodji Seketeli

Hi!

Just a couple of random comments:

On Wed, Oct 31, 2012 at 11:34:10AM -0700, Wei Mi wrote:
> gcc/ChangeLog:
> 2012-10-31  Wei Mi  <wmi@gmail.com>

If Dmitry wrote parts of the patch, it would be nice to mention
him in the ChangeLog too.

>         * 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

All ChangeLog entries should end with a dot.

> --- 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
>  <http://www.gnu.org/licenses/>.  */
>  
> +#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 */

Why this?  Simply don't include that header in tsan.c, it is already
included by basic-block.h.

> --- 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/
> + */

Can't google just assign the code to FSF, and use a standard boilerplate
as everything else in gcc/ ?

> +/* 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;
> +}
...

Instead of this (but same applies to asan), I think we should just consider
putting it into builtins.def (or have sanitizer.def like there is sync.def
or omp-builtins.def).  The problem might be non-C/C++ family frontends
though.

> +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;

int_size_in_bytes.

> +  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;

I think it is weird to return gimple_seqs, it would be better to just
emit the code at some stmt iterator, and preferrably without building
everything as trees, then gimplifying it.
> +}
> +
> +/* 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)

This really should use a stmt iterator.

> +  FOR_EACH_BB (bb)
> +    {
> +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> +        {
> +          instrument_gimple (gsi);
> +        }
> +    }

Extraneous two pairs of {}s.

> +  struct gimplify_ctx gctx;
> +
> +  func_calls = 0;
> +  func_mops = 0;

For gimplification context, see above, would be better to just
emit gimple directly.
For func_calls and func_mops, I believe why you need two variables instead
of just one, and why the function can't just return a bool whether
entry/exit needs to be instrumented or not.
> +  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 = {{

Please watch formatting of other gimple_opt_pass structures.
{{ isn't used anywhere.

> --- 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

Is that the option that LLVM uses (I'm talking about -faddress-sanitizer
in LLVM vs. -fasan right now in GCC, isn't that similar?).

	Jakub

^ permalink raw reply	[flat|nested] 56+ messages in thread

* [tsan] ThreadSanitizer instrumentation part
@ 2012-10-31 18:56 Wei Mi
  2012-10-31 23:17 ` Jakub Jelinek
  2012-11-01  6:58 ` Jakub Jelinek
  0 siblings, 2 replies; 56+ messages in thread
From: Wei Mi @ 2012-10-31 18:56 UTC (permalink / raw)
  To: gcc-patches
  Cc: Dmitry Vyukov, David Li, Diego Novillo, Jakub Jelinek, Dodji Seketeli

[-- Attachment #1: Type: text/plain, Size: 2518 bytes --]

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  <wmi@gmail.com>

        * 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.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 23152 bytes --]

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
 <http://www.gnu.org/licenses/>.  */
 
+#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 <dvyukov@google.com>
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+#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

[-- Attachment #3: race_on_heap.cc --]
[-- Type: application/octet-stream, Size: 1012 bytes --]

// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

void *Thread1(void *p) {
  *(int*)p = 42;
  return 0;
}

void *Thread2(void *p) {
  *(int*)p = 44;
  return 0;
}

void *alloc() {
  return malloc(99);
}

void *AllocThread(void* arg) {
  return alloc();
}

int main() {
  void *p = 0;
  pthread_t t[2];
  pthread_create(&t[0], 0, AllocThread, 0);
  pthread_join(t[0], &p);
  fprintf(stderr, "addr=%p\n", p);
  pthread_create(&t[0], 0, Thread1, (char*)p + 16);
  pthread_create(&t[1], 0, Thread2, (char*)p + 16);
  pthread_join(t[0], 0);
  pthread_join(t[1], 0);
  return 0;
}

// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race
// ...
// CHECK:   Location is heap block of size 99 at [[ADDR]] allocated by thread 1:
// CHCEKL     #0 malloc
// CHECK:     #1 alloc
// CHECK:     #2 AllocThread
// ...
// CHECK:   Thread 1 (tid={{.*}}, finished) created at:
// CHECK:     #0 pthread_create
// CHECK:     #1 main

^ permalink raw reply	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2012-12-03 16:58 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-01  6:00 [tsan] ThreadSanitizer instrumentation part Xinliang David Li
2012-11-01  6:27 ` Jakub Jelinek
2012-11-01 15:47   ` Xinliang David Li
     [not found] ` <CA+4CFy4uuCtFuiqai0b_VXib1te=DeBv1_EWDFjf8UP6MYMJPg@mail.gmail.com>
     [not found]   ` <CAAkRFZKWhxJP=0p-6JCgMrMGPTj4Vv3m9yNhRgioOKxzTsss1Q@mail.gmail.com>
     [not found]     ` <CA+4CFy7ecGOkmXsdkC=LbSK5KMdBSR6eT0oLmfn2nbyr0S2bMA@mail.gmail.com>
2012-11-03  1:31       ` Wei Mi
2012-11-03 17:05         ` Wei Mi
2012-11-03 18:39           ` Jakub Jelinek
2012-11-06  0:37             ` Wei Mi
2012-11-12 18:58               ` Wei Mi
2012-11-13 16:41               ` Jakub Jelinek
2012-11-13 17:25                 ` Xinliang David Li
2012-11-13 17:36                   ` Jakub Jelinek
2012-11-13 17:55                     ` Xinliang David Li
2012-11-14  0:08                 ` Wei Mi
2012-11-14  0:54                   ` Richard Henderson
2012-11-14  1:06                     ` Wei Mi
2012-11-16 23:13                       ` Wei Mi
2012-11-18 18:52                         ` Konstantin Serebryany
2012-11-22  7:23                           ` Wei Mi
     [not found]                             ` <CACT4Y+aR5vizMRb7VCXK7w=bnEz29g9WjQkjvu=8=kds610D1Q@mail.gmail.com>
     [not found]                               ` <CACT4Y+YrUg13dqtUTD23qEWnySNgQ1T_H06R2=ytpXKZeG8UUQ@mail.gmail.com>
2012-11-22  7:45                                 ` Xinliang David Li
     [not found]                                   ` <CACT4Y+a07z9-VtGKQ7gGCyHWb_XH4sVWFtPG=qpkk_gJXfc40A@mail.gmail.com>
2012-11-22 22:06                                     ` Wei Mi
2012-11-22  8:08                               ` Jakub Jelinek
2012-11-22 15:13                                 ` [PATCH] Convert asan to use sanitizer.def builtins, initialize them if the FE didn't Jakub Jelinek
2012-12-03 16:58                                   ` Dodji Seketeli
2012-11-22  9:54                             ` [tsan] ThreadSanitizer instrumentation part Jakub Jelinek
2012-11-22 13:21                               ` [tsan] libsanitizer tweaks Jakub Jelinek
2012-11-22 23:22                                 ` Diego Novillo
2012-11-22 22:08                               ` [tsan] ThreadSanitizer instrumentation part Wei Mi
2012-11-22 23:04                                 ` H.J. Lu
2012-11-22 23:06                                   ` Jakub Jelinek
2012-11-22 23:18                                 ` Jack Howarth
2012-11-23  0:53                                   ` Wei Mi
  -- strict thread matches above, loose matches on Subject: below --
2012-11-01 19:35 Xinliang David Li
2012-11-01 20:54 ` Jakub Jelinek
2012-11-01 20:57   ` Xinliang David Li
2012-11-01 21:07     ` Jakub Jelinek
2012-11-01 21:19       ` Xinliang David Li
2012-11-01 21:24         ` Jakub Jelinek
2012-11-01 21:43           ` Xinliang David Li
2012-11-01 18:11 Xinliang David Li
     [not found] ` <CACT4Y+azNYV+8ieRthd-SkQqGO82dj2+JOdL5c15DyBc9LwyfA@mail.gmail.com>
     [not found]   ` <CACT4Y+YQDBVxL2GdK9o-8kn===1XzX9JaBJUpM5N4MYCJAeo=Q@mail.gmail.com>
2012-11-01 18:20     ` Xinliang David Li
2012-11-01 18:22 ` Diego Novillo
2012-11-01 18:24 ` Jakub Jelinek
2012-11-01 18:32   ` Xinliang David Li
2012-11-01 18:49     ` Jakub Jelinek
2012-11-01 18:57       ` Xinliang David Li
2012-11-01 19:16         ` Jakub Jelinek
     [not found]           ` <CACT4Y+bbG3MOBX0vmP-Dvf=oPDp6GBDodJ+FbHrXEmjHjWn-qw@mail.gmail.com>
2012-11-01 20:53             ` Jakub Jelinek
2012-11-02 13:31 ` Martin Jambor
2012-11-02 15:54   ` Xinliang David Li
2012-11-02 15:58     ` Jakub Jelinek
2012-11-02 16:14       ` Xinliang David Li
2012-10-31 18:56 Wei Mi
2012-10-31 23:17 ` Jakub Jelinek
2012-11-01  6:02   ` Xinliang David Li
     [not found]   ` <CACT4Y+Z77tgge+k4+DOMkCLOWL4-xhate5Ccmav3Vox3_8Pqcw@mail.gmail.com>
2012-11-01  7:22     ` Jakub Jelinek
2012-11-01  6:58 ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).