#include <tree.h>
#include <ggc.h>
#include <basic-block.h>
+#include <tree-flow.h>
#include <gimple.h>
#include "aop.h"
+#include "aop-dynval.h"
#include "aop-pointcut.h"
#include "aop-type.h"
+/**
+ * Get the name of the variable being assigned to in an assignment
+ * joinpoint.
+ * \param jp An assignment joinpoint. Assignment joinpoints are
+ * obtained by joining on an aop_match_assignment_by_type() pointcut.
+ * \return For a direct assignment to a variable, the name of that
+ * variable, otherwise NULL.
+ */
const char *
aop_capture_lhs_name (struct aop_joinpoint *jp)
{
}
}
+/* Given an expression from the lhs of an assignment, return true
+ unless it is a temporary variable created by the compiler.
+
+ Generally, users will not be interested in instrumenting those temp
+ variables that return false here. */
+static bool
+lhs_is_real (tree lhs)
+{
+ if (TREE_CODE (lhs) == SSA_NAME)
+ lhs = SSA_NAME_VAR (lhs);
+
+ /* DECL_VAR nodes with DECL_ARTIFICIAL set represent temporary
+ variables. */
+ if (TREE_CODE (lhs) == VAR_DECL)
+ return !DECL_ARTIFICIAL (lhs);
+ else
+ return true;
+}
+
+/* This gets called when weaving an advice function that evaluates an
+ aop_capture_lhs_addr() dynval. It does the actual work of
+ inserting statements to get a reference to &lhs and then returns
+ that reference. */
+static tree
+op_get_lhs_addr (struct aop_dynval *dv)
+{
+ gimple stmt;
+ tree lhs;
+ tree lhs_pointer;
+
+ struct aop_joinpoint *jp = dv->jp;
+
+ aop_assert (dv->kind == ADV_LHS_ADDR);
+ aop_assert (jp->pc->kind == ATP_ASSIGN);
+
+ stmt = jp->stmt;
+ aop_assert (gimple_has_lhs (stmt));
+ lhs = gimple_get_lhs (stmt);
+
+ aop_assert (lhs_is_real (lhs));
+
+ if (TREE_CODE (lhs) == SSA_NAME)
+ lhs = SSA_NAME_VAR (lhs);
+
+ /* GCC only marks a variable as addressable if the program actually
+ takes its address (allowing certain optimizations on those
+ variables that are not addressable). In order to take the
+ address ourselves, we must make sure that the variable is marked
+ addressable. */
+ mark_addressable (lhs);
+ lhs_pointer = build1 (ADDR_EXPR, build_pointer_type (void_type_node),
+ stabilize_reference (lhs));
+
+ /* If mark_addressable() fails to make a variable addressable, it is
+ impossible to address that variable. */
+ aop_assert (is_gimple_address (lhs_pointer));
+
+ return lhs_pointer;
+}
+
+/**
+ * Get a dynval representing the address of the variable being
+ * assigned to at an assignment joinpoint.
+ * Note that this capture function will return NULL if the joinpoint
+ * is an assignment to a temporary variable: temporary variables are
+ * never addressable. You only need to worry about non-addressable
+ * temporary variables in a pointcut if you filter it with
+ * aop_filter_include_temps().
+ * \param jp An assignment joinpoint. Assignment joinpoints are
+ * obtained by joining on an aop_match_assignment_by_type() pointcut.
+ * \return A dynval with (void *) type or NULL if the assignment
+ * target is not addressable.
+ */
+struct aop_dynval *
+aop_capture_lhs_addr (struct aop_joinpoint *jp)
+{
+ tree lhs;
+ struct aop_dynval *dv;
+
+ aop_assert (jp->pc->kind == ATP_ASSIGN);
+
+ /* Is this a temporary? */
+ aop_assert (gimple_has_lhs (jp->stmt));
+ lhs = gimple_get_lhs (jp->stmt);
+ if (!lhs_is_real (lhs))
+ {
+ /* We should only see temp vars if this is a pointcut that
+ includes them. */
+ aop_assert (jp->pc->pc_assign.include_temp_vars);
+ return NULL;
+ }
+
+ dv = ggc_alloc (sizeof (struct aop_dynval));
+ dv->kind = ADV_LHS_ADDR;
+ dv->type = aop_t_all_pointer ();
+ dv->jp = jp;
+ dv->get_dynval = op_get_lhs_addr;
+
+ return dv;
+}
+
static bool
stmt_matches_pointcut (struct aop_pointcut *pc, gimple stmt)
{
if (gimple_has_lhs (stmt))
{
+ tree lhs = gimple_get_lhs(stmt);
tree type = TREE_TYPE (gimple_get_lhs (stmt));
- return does_type_match (type, pc->pc_assign.type);
+
+ /* Temp variables with matching type only match the pointcut if
+ include_temp_vars is true. */
+ return does_type_match (type, pc->pc_assign.type)
+ && (pc->pc_assign.include_temp_vars || lhs_is_real (lhs));
}
else
{
struct aop_joinpoint jp;
jp.pc = pc;
jp.gsi = &gsi;
+ jp.stmt = stmt;
cb (&jp, callback_param);
}
}
}
+/**
+ * Include compiler-created temporary variables in an assignment
+ * pointcut.
+ *
+ * By default, assignment pointcuts do not include assignments to
+ * temporary variables. These assignments will usually only be useful
+ * to users trying to debug the compiler with InterAsepct.
+ * \param pc The pointcut to modify with the filter.
+ */
+void
+aop_filter_include_temps (struct aop_pointcut *pc)
+{
+ aop_assert (pc->kind == ATP_ASSIGN);
+ pc->pc_assign.include_temp_vars = true;
+}
+
+/**
+ * Exclude compiler-created temporary variables from an assignment
+ * pointcut.
+ *
+ * This function is provided to undo the effect of
+ * aop_filter_include_temps(); by default, you do not need to call it.
+ * \param pc The pointcut to modify with the filter.
+ */
+void
+aop_filter_exclude_temps (struct aop_pointcut *pc)
+{
+ aop_assert (pc->kind == ATP_ASSIGN);
+ pc->pc_assign.include_temp_vars = false;
+}
+
+/**
+ * Return a pointcut that matches all assignments to a variable with
+ * the given type.
+ * \param type The type to filter assignments by.
+ * \return The resulting pointcut.
+ */
struct aop_pointcut *
aop_match_assignment_by_type (const struct aop_type *type)
{
struct aop_pointcut *pc;
- pc = ggc_alloc (sizeof (struct aop_pointcut *));
+ pc = ggc_alloc (sizeof (struct aop_pointcut));
pc->kind = ATP_ASSIGN;
pc->join_on = op_join_on_assign;
pc->insert_before = op_default_insert_before;
pc->pc_assign.type = type;
+ pc->pc_assign.include_temp_vars = false;
return pc;
}
/* This is a private header. Do not include it in source file for
client plug-ins. */
+#include "aop-callback.h"
+
struct aop_type;
enum aop_pckind {
ATP_EXIT,
};
+/* Information specific to assignment pointcuts. */
struct aop_pc_assign {
const struct aop_type *type;
+
+ /* If true, include assignments to temporary variables in the
+ pointcut. This is false by default. */
+ bool include_temp_vars;
};
-/* An AOP pointcut represents the a set of joinponts: locations in the
+/* An AOP pointcut represents a set of joinponts: locations in the
source code that are available for inserting instrumentation.
In practice, we do not directly store the set of joinpoints.
enum aop_pckind kind;
void (*join_on) (struct aop_pointcut *, join_callback, void *);
- void (*insert_before) (struct aop_joinpoint *, gimple);
+ insert_callback insert_before;
union {
struct aop_pc_assign pc_assign;
/* A GCC iterator for where weave functions can insert their
instrumentation.*/
gimple_stmt_iterator *gsi;
+
+ /* The GIMPLE statement being instrumented (where relevant). */
+ gimple stmt;
};
void op_default_insert_before (struct aop_joinpoint *jp, gimple stmt);