[2/3] Add a pass to automatically add ptwrite instrumentation

Message ID 20180212025317.28068-2-andi@firstfloor.org
State New
Headers show
Series
  • [1/3] Add PTWRITE builtins for x86
Related show

Commit Message

Andi Kleen Feb. 12, 2018, 2:53 a.m.
From: Andi Kleen <ak@linux.intel.com>

Add a new pass to automatically instrument changes to variables
with the new PTWRITE instruction on x86. PTWRITE writes a 4 or 8 byte
field into an external Processor Trace log.

This allows to reconstruct how values later, which can be useful for
debugging or other analysis of the program behavior. With the compiler
support this can be done with without having to manually add instrumentation
to the code.

Using dwarf information this can be later mapped back to the variables.

There are new options to enable instrumentation for different types,
and also a new attribute to control analysis fine grained per
function or variable level. The attributes can be set on both
the variable and the type level, and also on structure fields.
This allows to enable tracing only for specific code in large
programs.

The pass is generic, but only the x86 backend enables the necessary
hooks. When the backend enables the necessary hooks (with -mptwrite)
there is an additional pass that looks through the code for
attribute vartrace enabled functions or variables.

The -fvartrace-locals options is experimental: it works, but it
generates many redundant ptwrites because the pass doesn't use
the SSA information to minimize instrumentation. This could be optimized
later.

Currently the code can be tested with SDE, or on a Intel
Cherry Trail system with a new enough Linux kernel (v4.10+)
that supports PTWRITE for PT. Linux perf can be used to
record the values

perf record -e intel_pt/ptw=1,branch=0/ program
perf script --itrace=crw -F +synth ...

I have an experimential version of perf that can also use
dwarf information to symbolize many[1] values back to their variable
names. So far it is not in standard perf, but available at

https://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-misc.git/log/?h=perf/var-resolve-2

Longer term hopefully gdb will support this information too.

Passes bootstrap and test suite on x86_64-linux.

[1] Many: so far it only supports register variables that are not
arguments.

gcc/:

2018-02-10  Andi Kleen  <ak@linux.intel.com>

	* Makefile.in: Add tree-vartrace.o.
	* common.opt: Add -fvartrace, -fvartrace-returns,
	-fvartrace-args, -fvartrace-reads, -fvartrace-writes,
	-fvartrace-locals
	* config/i386/i386.c (ix86_vartrace_func): Add.
	(TARGET_VARTRACE_FUNC): Add.
	* doc/extend.texi: Document vartrace/no_vartrace
	attributes.
	* doc/invoke.texi: Document -fvartrace, -fvartrace-returns,
	-fvartrace-args, -fvartrace-reads, -fvartrace-writes,
	-fvartrace-locals
	* doc/tm.texi (TARGET_VARTRACE_FUNC): Add.
	* passes.def: Add vartrace pass.
	* target.def (vartrace_func): Add.
	* tree-pass.h (make_pass_vartrace): Add.
	* tree-vartrace.c: New file to implement vartrace pass.

gcc/c-family/:

2018-02-10  Andi Kleen  <ak@linux.intel.com>

	* c-attribs.c (handle_vartrace_attribute): New function.
---
 gcc/Makefile.in          |   1 +
 gcc/c-family/c-attribs.c |  23 +++
 gcc/common.opt           |  24 +++
 gcc/config/i386/i386.c   |  16 ++
 gcc/doc/extend.texi      |  13 ++
 gcc/doc/invoke.texi      |  29 +++
 gcc/doc/tm.texi          |   4 +
 gcc/doc/tm.texi.in       |   2 +
 gcc/passes.def           |   1 +
 gcc/target.def           |   7 +
 gcc/tree-pass.h          |   1 +
 gcc/tree-vartrace.c      | 462 +++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 583 insertions(+)
 create mode 100644 gcc/tree-vartrace.c

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6c37e46f792..3bce0f21bb4 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1580,6 +1580,7 @@  OBJS = \
 	tree-vectorizer.o \
 	tree-vector-builder.o \
 	tree-vrp.o \
+	tree-vartrace.o \
 	tree.o \
 	typed-splay-tree.o \
 	unique-ptr-tests.o \
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 0261a45ec98..0c6488e0912 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -104,6 +104,8 @@  static tree handle_tls_model_attribute (tree *, tree, tree, int,
 					bool *);
 static tree handle_no_instrument_function_attribute (tree *, tree,
 						     tree, int, bool *);
+static tree handle_vartrace_attribute (tree *, tree,
+						     tree, int, bool *);
 static tree handle_no_profile_instrument_function_attribute (tree *, tree,
 							     tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
@@ -331,6 +333,12 @@  const struct attribute_spec c_common_attribute_table[] =
   { "no_instrument_function", 0, 0, true,  false, false, false,
 			      handle_no_instrument_function_attribute,
 			      NULL },
+  { "vartrace", 	      0, 0, false,  false, false, false,
+			      handle_vartrace_attribute,
+			      NULL },
+  { "no_vartrace", 	      0, 0, false,  false, false, false,
+			      handle_vartrace_attribute,
+			      NULL },
   { "no_profile_instrument_function",  0, 0, true, false, false, false,
 			      handle_no_profile_instrument_function_attribute,
 			      NULL },
@@ -773,6 +781,21 @@  handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int,
   return NULL_TREE;
 }
 
+/* Handle "vartrace"/"no_vartrace" attributes; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_vartrace_attribute (tree *node, tree, tree, int flags,
+			   bool *)
+{
+  if (TYPE_P (*node) && !(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
+    *node = build_variant_type_copy (*node);
+
+  /* Can apply to types, functions, variables.  */
+  /* We lookup it up later with lookup_attribute.  */
+  return NULL_TREE;
+}
+
 /* Handle an "asan odr indicator" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 40ec0088c57..3bfcbb2937b 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2795,6 +2795,30 @@  ftree-scev-cprop
 Common Report Var(flag_tree_scev_cprop) Init(1) Optimization
 Enable copy propagation of scalar-evolution information.
 
+fvartrace
+Common Report Var(flag_vartrace)
+Generate all variable tracking instrumentations, except for locals.
+
+fvartrace-returns
+Common Report Var(flag_vartrace_returns)
+Generate variable tracking instructions for function returns.
+
+fvartrace-args
+Common Report Var(flag_vartrace_args)
+Generate variable tracking instructions for function arguments.
+
+fvartrace-reads
+Common Report Var(flag_vartrace_reads)
+Generate variable tracking instructions for reads.
+
+fvartrace-writes
+Common Report Var(flag_vartrace_writes)
+Generate variable tracking instructions for writes.
+
+fvartrace-locals
+Common Report Var(flag_vartrace_locals)
+Generate variable tracking instructions for locals.
+
 ; -fverbose-asm causes extra commentary information to be produced in
 ; the generated assembly code (to make it more readable).  This option
 ; is generally only of use to those who actually need to read the
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index d11d4909450..ca576f91f31 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -32534,6 +32534,19 @@  ix86_mangle_function_version_assembler_name (tree decl, tree id)
 }
 
 
+static tree
+ix86_vartrace_func (tree type)
+{
+  if (!(ix86_isa_flags2 & OPTION_MASK_ISA_PTWRITE))
+    return NULL;
+  if (TYPE_PRECISION (type) == 32)
+    return ix86_builtins [(int) IX86_BUILTIN_PTWRITE32];
+  else if (TYPE_PRECISION (type) == 64)
+    return ix86_builtins [(int) IX86_BUILTIN_PTWRITE64];
+  else
+    return NULL;
+}
+
 static tree 
 ix86_mangle_decl_assembler_name (tree decl, tree id)
 {
@@ -51585,6 +51598,9 @@  ix86_run_selftests (void)
 #undef TARGET_ASAN_SHADOW_OFFSET
 #define TARGET_ASAN_SHADOW_OFFSET ix86_asan_shadow_offset
 
+#undef TARGET_VARTRACE_FUNC
+#define TARGET_VARTRACE_FUNC ix86_vartrace_func
+
 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
 #define TARGET_GIMPLIFY_VA_ARG_EXPR ix86_gimplify_va_arg
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8efea867262..dd5279e0767 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3154,6 +3154,13 @@  the standard C library can be guaranteed not to throw an exception
 with the notable exceptions of @code{qsort} and @code{bsearch} that
 take function pointer arguments.
 
+@item no_vartrace
+@cindex @code{no_vartrace} function or variable attribute
+Disable data tracing for the function or variable or structured field
+marked with this attribute. Applies to types. Currently implemented
+for x86 when the @option{ptwrite} target option is enabled for systems
+that support the @code{PTWRITE} instruction.
+
 @item optimize
 @cindex @code{optimize} function attribute
 The @code{optimize} attribute is used to specify that a function is to
@@ -3396,6 +3403,12 @@  When applied to a member function of a C++ class template, the
 attribute also means that the function is instantiated if the
 class itself is instantiated.
 
+@item vartrace
+@cindex @code{vartrace} function or variable attribute
+Enable data tracing for the function or variable or structure field
+marked with this attribute. Applies to types. Will not trace locals,
+but arguments, returns, globals, pointer references.
+
 @item visibility ("@var{visibility_type}")
 @cindex @code{visibility} function attribute
 This attribute affects the linkage of the declaration to which it is attached.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 602cf8e3edc..3c5f1b5f7c8 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2686,6 +2686,35 @@  Don't use the @code{__cxa_get_exception_ptr} runtime routine.  This
 causes @code{std::uncaught_exception} to be incorrect, but is necessary
 if the runtime routine is not available.
 
+@item -fvartrace
+@opindex -fvartrace
+Insert trace instructions to trace variable values at runtime.
+Requires enabling a backend specific option, like @option{-mptwrite} to enable
+@code{PTWRITE} instruction generation on x86. @option{-fvartrace} traces
+arguments, return values, pointer references and globals, but no locals.
+
+@item -fvartrace-args
+@opindex -fvartrace-args
+Trace arguments. Can be used independently or together with @option{-vartrace},
+or as @option{-fno-vartrace-args} to disable.
+
+@item -fvartrace-returns
+@opindex -fvartrace-returns
+Trace return values.  Can be used independently or together with @option{-vartrace},
+or as @option{-fno-vartrace-return} to disable.
+
+@item -fvartrace-reads
+@opindex -fvartrace-reads
+Trace reads.
+
+@item -fvartrace-writes
+@opindex -fvartrace-writes
+Trace writes.
+
+@item -fvartrace-locals
+@opindex -fvartrace-locals
+Insert code to trace local variables. This can have high overhead.
+
 @item -fvisibility-inlines-hidden
 @opindex fvisibility-inlines-hidden
 This switch declares that the user does not attempt to compare
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index ddf48cb4b4d..94971b51340 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11903,6 +11903,10 @@  Address Sanitizer shadow memory address.  NULL if Address Sanitizer is not
 supported by the target.
 @end deftypefn
 
+@deftypefn {Target Hook} tree TARGET_VARTRACE_FUNC (tree @var{type})
+Return a builtin to call to trace variables or NULL if not supported by the target.
+@end deftypefn
+
 @deftypefn {Target Hook} {unsigned HOST_WIDE_INT} TARGET_MEMMODEL_CHECK (unsigned HOST_WIDE_INT @var{val})
 Validate target specific memory model mask bits. When NULL no target specific
 memory model bits are allowed.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 0aab45f4992..c001a78b43a 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8043,6 +8043,8 @@  and the associated definitions of those functions.
 
 @hook TARGET_ASAN_SHADOW_OFFSET
 
+@hook TARGET_VARTRACE_FUNC
+
 @hook TARGET_MEMMODEL_CHECK
 
 @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
diff --git a/gcc/passes.def b/gcc/passes.def
index 9802f08ecfc..97a1a4199b6 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -188,6 +188,7 @@  along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_oacc_device_lower);
   NEXT_PASS (pass_omp_device_lower);
   NEXT_PASS (pass_omp_target_link);
+  NEXT_PASS (pass_vartrace);
   NEXT_PASS (pass_all_optimizations);
   PUSH_INSERT_PASSES_WITHIN (pass_all_optimizations)
       NEXT_PASS (pass_remove_cgraph_callee_edges);
diff --git a/gcc/target.def b/gcc/target.def
index aeb41df1945..22a8b2e9f55 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4345,6 +4345,13 @@  supported by the target.",
  unsigned HOST_WIDE_INT, (void),
  NULL)
 
+/* Defines the builtin to trace variables, or NULL.  */
+DEFHOOK
+(vartrace_func,
+ "Return a builtin to call to trace variables or NULL if not supported by the target.",
+ tree, (tree type),
+ NULL)
+
 /* Functions relating to calls - argument passing, returns, etc.  */
 /* Members of struct call have no special macro prefix.  */
 HOOK_VECTOR (TARGET_CALLS, calls)
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 93a6a99eb7a..8f50bcc7c04 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -427,6 +427,7 @@  extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_stdarg (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_vartrace (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_early_warn_uninitialized (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_late_warn_uninitialized (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_cse_reciprocals (gcc::context *ctxt);
diff --git a/gcc/tree-vartrace.c b/gcc/tree-vartrace.c
new file mode 100644
index 00000000000..53b2740cc02
--- /dev/null
+++ b/gcc/tree-vartrace.c
@@ -0,0 +1,462 @@ 
+/* Insert instructions for data value tracing.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Andi Kleen.
+
+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 "backend.h"
+#include "target.h"
+#include "tree.h"
+#include "tree-iterator.h"
+#include "tree-pass.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimplify.h"
+#include "gimplify-me.h"
+#include "gimple-ssa.h"
+#include "gimple-pretty-print.h"
+#include "cfghooks.h"
+#include "ssa.h"
+#include "tree-dfa.h"
+#include "attribs.h"
+
+enum attrstate { force_off, force_on, neutral };
+
+/* Can we trace with attributes ATTR.  */
+
+static attrstate supported_attr (tree attr)
+{
+  if (lookup_attribute ("no_vartrace", attr))
+    return force_off;
+  if (lookup_attribute ("vartrace", attr))
+    return force_on;
+  return neutral;
+}
+
+/* Is ARG supported considering S, handling both decls and other trees.  */
+
+static attrstate supported_op (tree arg, attrstate s)
+{
+  if (s != neutral)
+    return s;
+  if (DECL_P (arg))
+    {
+      s = supported_attr (DECL_ATTRIBUTES (arg));
+      if (s != neutral)
+	return s;
+    }
+  return supported_attr (TYPE_ATTRIBUTES (TREE_TYPE (arg)));
+}
+
+/* Can we trace T.  */
+
+static attrstate supported_type (tree t)
+{
+  tree type = TREE_TYPE (t);
+
+  if (!POINTER_TYPE_P (type) && !INTEGRAL_TYPE_P (type))
+    return force_off;
+  enum attrstate s = supported_op (t, neutral);
+  if (TREE_CODE (t) == COMPONENT_REF
+	   || TREE_CODE (t) == ARRAY_REF)
+    {
+      s = supported_op (TREE_OPERAND (t, 0), s);
+      s = supported_op (TREE_OPERAND (t, 1), s);
+    }
+  return s;
+}
+
+/* Can we trace T, or if FORCE is set.  */
+
+static bool supported_type_or_force (tree t, bool force)
+{
+  enum attrstate s = supported_type (t);
+  if (s == neutral)
+    return force;
+  return s == force_off ? false : true;
+}
+
+/* Return true if T refering to a local variable.
+   ?? better ways to do this?  */
+
+static bool is_local (tree t)
+{
+  // Add another attribute to override?
+  if (!flag_vartrace_locals)
+    return false;
+  if (TREE_STATIC (t))
+    return false;
+  if (TREE_CODE (t) == VAR_DECL && DECL_EXTERNAL (t))
+    return false;
+  return true;
+}
+
+/* Is T something we can log, FORCEing the type if needed.  */
+
+static bool supported_mem (tree t, bool force)
+{
+  enum attrstate s = supported_type (t);
+
+  if (s == force_off)
+    return false;
+
+  switch (TREE_CODE (t))
+    {
+    case VAR_DECL:
+      if (DECL_ARTIFICIAL (t))
+	return false;
+      if (is_local (t))
+	return true;
+      return s == force_on || force;
+
+    case ARRAY_REF:
+    case COMPONENT_REF:
+      t = TREE_OPERAND (t, 0);
+      if (is_local (t))
+	return true;
+      return s == force_on || force;
+
+    case TARGET_MEM_REF:
+    case MEM_REF:
+      // could use points-to to check for locals?
+      return true;
+
+    case SSA_NAME:
+      if (flag_vartrace_locals && is_gimple_reg (t))
+	return true;
+      break;
+
+    default:
+      break;
+    }
+
+  return false;
+}
+
+/* Print debugging for inserting CALL at ORIG_STMT with type of VAL.  */
+
+static void log_trace_code (gimple *orig_stmt, gimple *code,
+			    tree val)
+{
+  if (dump_file)
+    {
+      if (orig_stmt)
+	fprintf (dump_file, "BB%d ", gimple_bb (orig_stmt)->index);
+      fprintf (dump_file, "inserting ");
+      print_gimple_stmt (dump_file, code, 0, TDF_VOPS|TDF_MEMSYMS);
+      if (orig_stmt)
+	{
+	  fprintf (dump_file, "orig ");
+	  print_gimple_stmt (dump_file, orig_stmt, 2,
+			     TDF_VOPS|TDF_MEMSYMS);
+	}
+      fprintf (dump_file, "type ");
+      print_generic_expr (dump_file, TREE_TYPE (val), 0);
+      fputc ('\n', dump_file);
+      fputc ('\n', dump_file);
+    }
+}
+
+/* Insert variable tracing code for VAL before iterator GI, originally
+   for ORIG_STMT.  Return trace variable or NULL.  */
+
+static tree insert_trace (gimple_stmt_iterator *gi, tree val,
+			  gimple *orig_stmt)
+{
+  tree func = targetm.vartrace_func (TREE_TYPE (val));
+  if (!func)
+    return NULL_TREE;
+
+  location_t loc = gimple_location (orig_stmt);
+
+  gimple_seq seq = NULL;
+  tree tvar = make_ssa_name (TREE_TYPE (val));
+  gassign *assign = gimple_build_assign (tvar, val);
+  log_trace_code (orig_stmt, assign, val);
+  gimple_set_location (assign, loc);
+  gimple_seq_add_stmt (&seq, assign);
+
+  gcall *call = gimple_build_call (func, 1, tvar);
+  log_trace_code (NULL, call, tvar);
+  gimple_set_location (call, loc);
+  gimple_seq_add_stmt (&seq, call);
+
+  gsi_insert_seq_before (gi, seq, GSI_SAME_STMT);
+  return tvar;
+}
+
+/* Insert trace at GI for T in FUN if suitable memory or variable reference.
+   Always if FORCE. Originally on ORIG_STMT.  */
+
+tree instrument_mem (gimple_stmt_iterator *gi, tree t,
+		     bool force,
+		     gimple *orig_stmt)
+{
+  if (supported_mem (t, force))
+    return insert_trace (gi, t, orig_stmt);
+  return NULL_TREE;
+}
+
+/* Instrument arguments for FUN considering FORCE. Return true if
+   function has changed.  */
+
+bool instrument_args (function *fun, bool force)
+{
+  bool changed = false;
+  gimple_stmt_iterator gi;
+
+  /* Local tracing usually takes care of the argument too, when
+     they are read. This avoids redundant trace instructions.  */
+  if (flag_vartrace_locals)
+    return false;
+
+  for (tree arg = DECL_ARGUMENTS (current_function_decl);
+       arg != NULL_TREE;
+       arg = DECL_CHAIN (arg))
+    {
+     gi = gsi_start_bb (BASIC_BLOCK_FOR_FN (fun, NUM_FIXED_BLOCKS));
+     if (supported_type_or_force (arg, force || flag_vartrace_args))
+	{
+	  tree func = targetm.vartrace_func (TREE_TYPE (arg));
+	  if (!func)
+	    continue;
+
+	  tree sarg = ssa_default_def (fun, arg);
+	  /* This can happen with tail recursion. Don't log in this
+	     case for now.  */
+	  if (!sarg)
+	    continue;
+
+	  if (has_zero_uses (sarg))
+	    continue;
+
+	  gimple_seq seq = NULL;
+	  tree tvar = make_ssa_name (TREE_TYPE (sarg));
+	  gassign *assign = gimple_build_assign (tvar, sarg);
+	  gimple_set_location (assign, fun->function_start_locus);
+	  gimple_seq_add_stmt (&seq, assign);
+
+	  gcall *call = gimple_build_call (func, 1, tvar);
+	  log_trace_code (NULL, call, tvar);
+	  gimple_set_location (call, fun->function_start_locus);
+	  gimple_seq_add_stmt (&seq, call);
+
+	  edge edge = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fun));
+	  gsi_insert_seq_on_edge_immediate (edge, seq);
+
+	  changed = true;
+	}
+    }
+  return changed;
+}
+
+/* Generate trace call for store STMT at GI, force if FORCE.  Return true
+   if successfull. Modifies the original store to use a temporary.  */
+
+static bool instrument_store (gimple_stmt_iterator *gi, gimple *stmt, bool force)
+{
+  if (!supported_mem (gimple_assign_lhs (stmt), force))
+    return false;
+
+  tree orig_tgt = gimple_assign_lhs (stmt);
+
+  tree func = targetm.vartrace_func (TREE_TYPE (orig_tgt));
+  if (!func)
+    return false;
+
+  tree new_tgt = make_ssa_name(TREE_TYPE (orig_tgt));
+  gimple_assign_set_lhs (stmt, new_tgt);
+  update_stmt (stmt);
+  log_trace_code (NULL, stmt, new_tgt);
+
+  gcall *tcall = gimple_build_call (func, 1, new_tgt);
+  log_trace_code (stmt, tcall, new_tgt);
+  gimple_set_location (tcall, gimple_location (stmt));
+  gsi_insert_after (gi, tcall, GSI_CONTINUE_LINKING);
+
+  gassign *new_store = gimple_build_assign (orig_tgt, new_tgt);
+  gimple_set_location (new_store, gimple_location (stmt));
+  log_trace_code (NULL, new_store, new_tgt);
+  gsi_insert_after (gi, new_store, GSI_CONTINUE_LINKING);
+  return true;
+}
+
+/* Instrument STMT at GI. Force if FORCE. CHANGED is the previous changed
+   state, which is also returned.  */
+
+bool instrument_assign (gimple_stmt_iterator *gi,
+			gimple *stmt, bool changed, bool force)
+{
+  gassign *gas = as_a <gassign *> (stmt);
+  bool read_force = force || flag_vartrace_reads;
+  tree t;
+
+  t = instrument_mem (gi, gimple_assign_rhs1 (gas),
+		      read_force,
+		      stmt);
+  if (t)
+    {
+      gimple_assign_set_rhs1 (gas, t);
+      changed = true;
+    }
+  if (gimple_num_ops (gas) > 2)
+    {
+      t = instrument_mem (gi, gimple_assign_rhs2 (gas),
+			  read_force,
+			  stmt);
+      if (t)
+	{
+	  gimple_assign_set_rhs2 (gas, t);
+	  changed = true;
+	}
+    }
+  if (gimple_num_ops (gas) > 3)
+    {
+      t = instrument_mem (gi, gimple_assign_rhs3 (gas),
+			  read_force,
+			  stmt);
+      if (t)
+	{
+	  gimple_assign_set_rhs3 (gas, t);
+	  changed = true;
+	}
+      }
+  if (gimple_num_ops (gas) > 4)
+    gcc_unreachable ();
+  if (gimple_store_p (stmt))
+    changed |= instrument_store (gi, stmt, flag_vartrace_writes || force);
+  if (changed)
+    update_stmt (stmt);
+  return changed;
+}
+
+/* Instrument return in function FUN at statement STMT at GI, force if
+   FORCE.  CHANGED is the changed flag, which is also returned.  */
+
+static bool instrument_return (function *fun,
+			       gimple_stmt_iterator *gi,
+			       gimple *stmt, bool changed,
+			       bool force)
+{
+  tree restype = TREE_TYPE (TREE_TYPE (fun->decl));
+  greturn *gret = as_a <greturn *> (stmt);
+  tree rval = gimple_return_retval (gret);
+
+  /* Cannot handle complex C++ return values at this point, even
+     if they would collapse to a valid trace type.  */
+  if (rval
+      && useless_type_conversion_p (restype, TREE_TYPE (rval))
+      && supported_type_or_force (rval, flag_vartrace_returns || force))
+    {
+      if (tree tvar = insert_trace (gi, rval, stmt))
+	{
+	  changed = true;
+	  gimple_return_set_retval (gret, tvar);
+	  log_trace_code (NULL, gret, tvar);
+	  update_stmt (stmt);
+	}
+    }
+
+  return changed;
+}
+
+/* Insert vartrace calls for FUN.  */
+
+static unsigned int vartrace_execute (function *fun)
+{
+  basic_block bb;
+  gimple_stmt_iterator gi;
+  bool force = flag_vartrace;
+  bool changed;
+
+  if (lookup_attribute ("vartrace", TYPE_ATTRIBUTES (TREE_TYPE (fun->decl)))
+      || lookup_attribute ("vartrace", DECL_ATTRIBUTES (fun->decl)))
+    force = true;
+
+  changed = instrument_args (fun, force);
+
+  FOR_ALL_BB_FN (bb, fun)
+    for (gi = gsi_start_bb (bb); !gsi_end_p (gi); gsi_next (&gi))
+      {
+	gimple *stmt = gsi_stmt (gi);
+	if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt))
+	  changed = instrument_assign (&gi, stmt, changed, force);
+	else if (gimple_code (stmt) == GIMPLE_RETURN)
+	  {
+	    changed = instrument_return (fun, &gi, stmt, changed, force);
+	    // must end basic block
+	    break;
+	  }
+
+	// instrument something else like CALL?
+	// We assume everything interesting is in a GIMPLE_ASSIGN
+      }
+  return changed ? TODO_update_ssa : 0;
+}
+
+const pass_data pass_data_vartrace =
+{
+  GIMPLE_PASS, /* type */
+  "vartrace", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_vartrace : public gimple_opt_pass
+{
+public:
+  pass_vartrace (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_vartrace, ctxt)
+  {}
+
+  virtual opt_pass * clone ()
+    {
+      return new pass_vartrace (m_ctxt);
+    }
+
+  virtual bool gate (function *fun)
+    {
+      // check if vartrace is supported in backend
+      if (!targetm.vartrace_func ||
+	  targetm.vartrace_func (integer_type_node) == NULL)
+	return false;
+
+      if (lookup_attribute ("no_vartrace", TYPE_ATTRIBUTES (TREE_TYPE (fun->decl)))
+	  || lookup_attribute ("no_vartrace", DECL_ATTRIBUTES (fun->decl)))
+	return false;
+
+      // need to run pass always to check for variable attributes
+      return true;
+    }
+
+  virtual unsigned int execute (function *f) { return vartrace_execute (f); }
+};
+
+gimple_opt_pass *
+make_pass_vartrace (gcc::context *ctxt)
+{
+  return new pass_vartrace (ctxt);
+}