#include "tree-ssa-propagate.h"
#include "target.h"
+/* Return true when DECL can be referenced from current unit.
+ We can get declarations that are not possible to reference for
+ various reasons:
+
+ 1) When analyzing C++ virtual tables.
+ C++ virtual tables do have known constructors even
+ when they are keyed to other compilation unit.
+ Those tables can contain pointers to methods and vars
+ in other units. Those methods have both STATIC and EXTERNAL
+ set.
+ 2) In WHOPR mode devirtualization might lead to reference
+ to method that was partitioned elsehwere.
+ In this case we have static VAR_DECL or FUNCTION_DECL
+ that has no corresponding callgraph/varpool node
+ declaring the body.
+ 3) COMDAT functions referred by external vtables that
+ we devirtualize only during final copmilation stage.
+ At this time we already decided that we will not output
+ the function body and thus we can't reference the symbol
+ directly. */
+
+static bool
+can_refer_decl_in_current_unit_p (tree decl)
+{
+ struct varpool_node *vnode;
+ struct cgraph_node *node;
+
+ if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+ return true;
+ /* External flag is set, so we deal with C++ reference
+ to static object from other file. */
+ if (DECL_EXTERNAL (decl) && TREE_STATIC (decl)
+ && TREE_CODE (decl) == VAR_DECL)
+ {
+ /* Just be sure it is not big in frontend setting
+ flags incorrectly. Those variables should never
+ be finalized. */
+ gcc_checking_assert (!(vnode = varpool_get_node (decl))
+ || !vnode->finalized);
+ return false;
+ }
+ /* When function is public, we always can introduce new reference.
+ Exception are the COMDAT functions where introducing a direct
+ reference imply need to include function body in the curren tunit. */
+ if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
+ return true;
+ /* We are not at ltrans stage; so don't worry about WHOPR.
+ Also when still gimplifying all referred comdat functions will be
+ produced. */
+ if (!flag_ltrans && (!DECL_COMDAT (decl) || !cgraph_function_flags_ready))
+ return true;
+ /* If we already output the function body, we are safe. */
+ if (TREE_ASM_WRITTEN (decl))
+ return true;
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ node = cgraph_get_node (decl);
+ /* Check that we still have function body and that we didn't took
+ the decision to eliminate offline copy of the function yet.
+ The second is important when devirtualization happens during final
+ compilation stage when making a new reference no longer makes callee
+ to be compiled. */
+ if (!node || !node->analyzed || node->global.inlined_to)
+ return false;
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ vnode = varpool_get_node (decl);
+ if (!vnode || !vnode->finalized)
+ return false;
+ }
+ return true;
+}
+
/* CVAL is value taken from DECL_INITIAL of variable. Try to transorm it into
acceptable form for is_gimple_min_invariant. */
if (TREE_CODE (cval) == ADDR_EXPR)
{
tree base = get_base_address (TREE_OPERAND (cval, 0));
+
+ if (base
+ && (TREE_CODE (base) == VAR_DECL
+ || TREE_CODE (base) == FUNCTION_DECL)
+ && !can_refer_decl_in_current_unit_p (base))
+ return NULL_TREE;
if (base && TREE_CODE (base) == VAR_DECL)
add_referenced_var (base);
}
tree
get_symbol_constant_value (tree sym)
{
- if ((TREE_STATIC (sym) || DECL_EXTERNAL (sym))
- && (TREE_CODE (sym) == CONST_DECL
- || varpool_get_node (sym)->const_value_known))
+ if (const_value_known_p (sym))
{
tree val = DECL_INITIAL (sym);
if (val)
{
val = canonicalize_constructor_val (val);
- if (is_gimple_min_invariant (val))
+ if (val && is_gimple_min_invariant (val))
return val;
+ else
+ return NULL_TREE;
}
/* Variables declared 'const' without an initializer
have zero as the initializer if they may not be
push_gimplify_context (&gctx);
if (lhs == NULL_TREE)
- gimplify_and_add (expr, &stmts);
+ {
+ gimplify_and_add (expr, &stmts);
+ /* We can end up with folding a memcpy of an empty class assignment
+ which gets optimized away by C++ gimplification. */
+ if (gimple_seq_empty_p (stmts))
+ {
+ if (gimple_in_ssa_p (cfun))
+ {
+ unlink_stmt_vdef (stmt);
+ release_defs (stmt);
+ }
+ gsi_remove (si_p, true);
+ return;
+ }
+ }
else
tmp = get_initialized_tmp_var (expr, &stmts, NULL);
if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = new_stmt;
}
+ else if (reaching_vuse == gimple_vuse (stmt))
+ unlink_stmt_vdef (stmt);
}
gimple_set_location (new_stmt, gimple_location (stmt));
gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_binfo)
{
HOST_WIDE_INT i;
- tree v, fndecl;
- struct cgraph_node *node;
+ tree v, fndecl, delta;
v = BINFO_VIRTUALS (known_binfo);
i = 0;
}
fndecl = TREE_VALUE (v);
- node = cgraph_get_node (fndecl);
+ delta = TREE_PURPOSE (v);
+ gcc_assert (host_integerp (delta, 0));
+
+ if (integer_nonzerop (delta))
+ {
+ struct cgraph_node *node = cgraph_get_node (fndecl);
+ HOST_WIDE_INT off = tree_low_cst (delta, 0);
+
+ if (!node)
+ return NULL;
+ for (node = node->same_body; node; node = node->next)
+ if (node->thunk.thunk_p && off == node->thunk.fixed_offset)
+ break;
+ if (node)
+ fndecl = node->decl;
+ else
+ return NULL;
+ }
+
/* When cgraph node is missing and function is not public, we cannot
devirtualize. This can happen in WHOPR when the actual method
ends up in other partition, because we found devirtualization
possibility too late. */
- if ((!node || (!node->analyzed && !node->in_other_partition))
- && (!TREE_PUBLIC (fndecl) || DECL_COMDAT (fndecl)))
+ if (!can_refer_decl_in_current_unit_p (fndecl))
return NULL;
return build_fold_addr_expr (fndecl);
}
if (binfo)
{
HOST_WIDE_INT token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
+ /* If there is no virtual methods leave the OBJ_TYPE_REF alone. */
+ if (!BINFO_VIRTUALS (binfo))
+ return NULL_TREE;
return gimple_fold_obj_type_ref_known_binfo (token, binfo);
}
else
It is assumed that the operands have been previously folded. */
static bool
-fold_gimple_call (gimple_stmt_iterator *gsi)
+fold_gimple_call (gimple_stmt_iterator *gsi, bool inplace)
{
gimple stmt = gsi_stmt (*gsi);
/* Check for builtins that CCP can handle using information not
available in the generic fold routines. */
- if (callee && DECL_BUILT_IN (callee))
+ if (!inplace && callee && DECL_BUILT_IN (callee))
{
tree result = gimple_fold_builtin (stmt);
there requires that we create a new CALL_EXPR, and that requires
copying EH region info to the new node. Easier to just do it
here where we can just smash the call operand. */
- /* ??? Is there a good reason not to do this in fold_stmt_inplace? */
callee = gimple_call_fn (stmt);
if (TREE_CODE (callee) == OBJ_TYPE_REF
&& TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR)
changed = true;
}
}
- /* The entire statement may be replaced in this case. */
- if (!inplace)
- changed |= fold_gimple_call (gsi);
+ changed |= fold_gimple_call (gsi, inplace);
break;
case GIMPLE_ASM: