From patchwork Sun Nov 4 06:32:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 992707 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-488956-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=firstfloor.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Xdvk0Nwb"; dkim=pass (1024-bit key; unprotected) header.d=firstfloor.org header.i=@firstfloor.org header.b="yeBTw4qL"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42nmGW4vz3zB4kd for ; Sun, 4 Nov 2018 17:33:23 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; q=dns; s=default; b=T3y v7zG6VBzONraw+LhksKNwFsikN2XiA7kPMxqsyeEst7I49EOKA46KQ7wbaW9H4id RohF6brWCW2B2SK3vfzuOa010r/ZnBmuSeItZZplRxXZfhbX/4oe5iwfXltgW9uS dHR3b1tWNVKzz0GVoHSh6Z2NpPa+vFTyCeAtlVf4= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=default; bh=X+erw7jl9 ZTjb/232cE8x2ytdJU=; b=Xdvk0NwbOeZwVyUFemx0YslShvhRH/xjZPBs71QP/ PgDbOT+VOAIQW7IVuwd1+nezFDHLG8FjGDEzpD6mm7d1E9F2ft+B28GRWRxkPwhN MEYhgdesgFVBnnUsa3m0m4fshfnahCK+jQPJwi+5tiociSNIFManmbPRqKDp1RCg qM= Received: (qmail 14746 invoked by alias); 4 Nov 2018 06:33:04 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 14244 invoked by uid 89); 4 Nov 2018 06:32:55 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.2 required=5.0 tests=AWL, BAYES_20, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 spammy=hopefully, type_p, *fun, stmt X-HELO: one.firstfloor.org Received: from one.firstfloor.org (HELO one.firstfloor.org) (193.170.194.197) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 04 Nov 2018 06:32:51 +0000 Received: from firstfloor.org (c-71-238-43-142.hsd1.or.comcast.net [71.238.43.142]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by one.firstfloor.org (Postfix) with ESMTPSA id 89C0F86862; Sun, 4 Nov 2018 07:32:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=firstfloor.org; s=mail; t=1541313166; bh=KaAgQfJkwgVs858POxhHfff1q7EgVaux0Vie7aNNd+Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=yeBTw4qLWYvOmfZwh4eCQK3ZBkwJpdNrvO9CA5oi88rxO+k+EdCs/Tnx2lQEmpT33 UW+MKQodZ8FK/gnV1kZeCXlpQsbh2uwAbSdT1ijQdnL5OaqMdxpiNuwkvyprqVUkbH fSq53Y793SGv8zocJdb2gIDohnXb9Jmf9jZW8gPs= Received: by firstfloor.org (Postfix, from userid 1000) id 065D9A2D56; Sat, 3 Nov 2018 23:32:43 -0700 (PDT) From: Andi Kleen To: gcc-patches@gcc.gnu.org Cc: Andi Kleen Subject: [PATCH 2/3] Add a pass to automatically add ptwrite instrumentation Date: Sat, 3 Nov 2018 23:32:34 -0700 Message-Id: <20181104063235.6914-2-andi@firstfloor.org> In-Reply-To: <20181104063235.6914-1-andi@firstfloor.org> References: <20181104063235.6914-1-andi@firstfloor.org> MIME-Version: 1.0 From: Andi Kleen 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 Processor Trace log, which allows log over head logging of informatin. 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 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 Gemini Lake 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-4 It is currently not able to decode all variable locations to names, but a large subset. Longer term hopefully gdb will support this information too. The CPU can potentially generate very data high bandwidths when code doing a lot of computation is heavily instrumented. This can cause some data loss in both the CPU and also in perf logging the data when the disk cannot keep up. Running some larger workloads most workloads do not cause CPU level overflows, but I've seen it with -fvartrace with crafty, and with more workloads with -fvartrace-locals. Recommendation is to not fully instrument programs, but only areas of interest either at the file level or using the attributes. The other thing is that perf and the disk often cannot keep up with the data bandwidth for longer computations. In this case it's possible to use perf snapshot mode (add --snapshot to the command line above). The data will be only logged to a memory ring buffer then, and only dump the buffers on events of interest by sending SIGUSR2 to the perf binrary. In the future this will be hopefully better supported with core files and gdb. Passes bootstrap and test suite on x86_64-linux, also bootstrapped and tested gcc itself with full -fvartrace and -fvartrace-locals instrumentation. gcc/: 2018-11-03 Andi Kleen * 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-11-03 Andi Kleen * c-attribs.c (handle_vartrace_attribute): New function. config/: 2018-11-03 Andi Kleen * bootstrap-vartrace.mk: New. * bootstrap-vartrace-locals.mk: New. --- config/bootstrap-vartrace-locals.mk | 3 + config/bootstrap-vartrace.mk | 3 + 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 | 463 ++++++++++++++++++++++++++++ 14 files changed, 590 insertions(+) create mode 100644 config/bootstrap-vartrace-locals.mk create mode 100644 config/bootstrap-vartrace.mk create mode 100644 gcc/tree-vartrace.c 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 +. */ + +#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 (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 (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); +}