diff --git a/config/bootstrap-vartrace-locals.mk b/config/bootstrap-vartrace-locals.mk
new file mode 100644
index 00000000000..c6c79e21120
--- /dev/null
+++ b/config/bootstrap-vartrace-locals.mk
@@ -0,0 +1,3 @@
+STAGE2_CFLAGS += -mptwrite -fvartrace -fvartrace-locals
+STAGE3_CFLAGS += -mptwrite -fvartrace -fvartrace-locals
+STAGE4_CFLAGS += -mptwrite -fvartrace -fvartrace-locals
diff --git a/config/bootstrap-vartrace.mk b/config/bootstrap-vartrace.mk
new file mode 100644
index 00000000000..e29824d799b
--- /dev/null
+++ b/config/bootstrap-vartrace.mk
@@ -0,0 +1,3 @@
+STAGE2_CFLAGS += -mptwrite -fvartrace
+STAGE3_CFLAGS += -mptwrite -fvartrace
+STAGE4_CFLAGS += -mptwrite -fvartrace
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 719a516c356..46aa4800e57 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1594,6 +1594,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 4416b5042f7..66bbd87921f 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 *);
@@ -325,6 +327,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 },
@@ -767,6 +775,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 2971dc21b1f..930acf40588 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2811,6 +2811,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 490bb6292a8..4337121c239 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -31873,6 +31873,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)
 {
@@ -50849,6 +50862,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 1eca009e255..08286aa4591 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3193,6 +3193,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 (@var{level}, @dots{})
 @item optimize (@var{string}, @dots{})
 @cindex @code{optimize} function attribute
@@ -3454,6 +3461,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 cb5bc7bafc5..2f10b3c1023 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2720,6 +2720,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 f841527f971..6555cb122e9 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11933,6 +11933,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 967ef3ad22f..7cce21bb26c 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8101,6 +8101,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 24f212c8e31..518cb4ef6f7 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -179,6 +179,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 ad27d352ca4..db5d88efb95 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4300,6 +4300,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 af15adc8e0c..2cf31785a6f 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -423,6 +423,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..07f5aa6bc8f
--- /dev/null
+++ b/gcc/tree-vartrace.c
@@ -0,0 +1,463 @@
+/* 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), TDF_SLIM);
+      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 = NULL;
+	  // ??? or force like sanopt?
+	  if (is_gimple_reg (arg))
+	    sarg = get_or_create_ssa_default_def (fun, arg);
+	  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);
+}
