diff options
author | Martin Jambor <mjambor@suse.cz> | 2011-04-19 18:35:33 +0200 |
---|---|---|
committer | Martin Jambor <jamborm@gcc.gnu.org> | 2011-04-19 18:35:33 +0200 |
commit | 49c471e3d8e5a505b20672f7029d07bb828cbe1b (patch) | |
tree | f6f04a6735e023b75b60f7849bf8007b8c56b69d /gcc/gimple-fold.c | |
parent | 9714ca724859e90773df206f552937ddc4da004c (diff) | |
download | gcc-49c471e3d8e5a505b20672f7029d07bb828cbe1b.tar.gz |
ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize also according to actual contants.
2011-04-19 Martin Jambor <mjambor@suse.cz>
* ipa-cp.c (ipcp_process_devirtualization_opportunities): Devirtualize
also according to actual contants.
* gimple-fold.c (gimple_extract_devirt_binfo_from_cst): New function.
(gimple_fold_call): Use it.
* gimple.h (gimple_extract_devirt_binfo_from_cst): Declare.
* testsuite/g++.dg/opt/devirt1.C: Bump to -O2, remove XFAIL.
* testsuite/g++.dg/opt/devirt2.C: New test.
* testsuite/g++.dg/ipa/devirt-g-1.C: Likewise.
From-SVN: r172719
Diffstat (limited to 'gcc/gimple-fold.c')
-rw-r--r-- | gcc/gimple-fold.c | 91 |
1 files changed, 88 insertions, 3 deletions
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index a6e326bdd1d..8d3ab6eb1ff 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -1445,6 +1445,74 @@ gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta) gimple_call_set_arg (call_stmt, 0, tmp); } +/* Return a binfo to be used for devirtualization of calls based on an object + represented by a declaration (i.e. a global or automatically allocated one) + or NULL if it cannot be found or is not safe. CST is expected to be an + ADDR_EXPR of such object or the function will return NULL. Currently it is + safe to use such binfo only if it has no base binfo (i.e. no ancestors). */ + +tree +gimple_extract_devirt_binfo_from_cst (tree cst) +{ + HOST_WIDE_INT offset, size, max_size; + tree base, type, expected_type, binfo; + bool last_artificial = false; + + if (!flag_devirtualize + || TREE_CODE (cst) != ADDR_EXPR + || TREE_CODE (TREE_TYPE (TREE_TYPE (cst))) != RECORD_TYPE) + return NULL_TREE; + + cst = TREE_OPERAND (cst, 0); + expected_type = TREE_TYPE (cst); + base = get_ref_base_and_extent (cst, &offset, &size, &max_size); + type = TREE_TYPE (base); + if (!DECL_P (base) + || max_size == -1 + || max_size != size + || TREE_CODE (type) != RECORD_TYPE) + return NULL_TREE; + + /* Find the sub-object the constant actually refers to and mark whether it is + an artificial one (as opposed to a user-defined one). */ + while (true) + { + HOST_WIDE_INT pos, size; + tree fld; + + if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (expected_type)) + break; + if (offset < 0) + return NULL_TREE; + + for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) + { + if (TREE_CODE (fld) != FIELD_DECL) + continue; + + pos = int_bit_position (fld); + size = tree_low_cst (DECL_SIZE (fld), 1); + if (pos <= offset && (pos + size) > offset) + break; + } + if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE) + return NULL_TREE; + + last_artificial = DECL_ARTIFICIAL (fld); + type = TREE_TYPE (fld); + offset -= pos; + } + /* Artifical sub-objects are ancestors, we do not want to use them for + devirtualization, at least not here. */ + if (last_artificial) + return NULL_TREE; + binfo = TYPE_BINFO (type); + if (!binfo || BINFO_N_BASE_BINFOS (binfo) > 0) + return NULL_TREE; + else + return binfo; +} + /* Attempt to fold a call statement referenced by the statement iterator GSI. The statement may be replaced by another statement, e.g., if the call simplifies to a constant value. Return true if any changes were made. @@ -1473,10 +1541,27 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) /* Check for virtual calls that became direct calls. */ callee = gimple_call_fn (stmt); - if (TREE_CODE (callee) == OBJ_TYPE_REF - && gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE) + if (TREE_CODE (callee) == OBJ_TYPE_REF) { - gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee)); + tree binfo, fndecl, delta, obj; + HOST_WIDE_INT token; + + if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE) + { + gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee)); + return true; + } + + obj = OBJ_TYPE_REF_OBJECT (callee); + binfo = gimple_extract_devirt_binfo_from_cst (obj); + if (!binfo) + return false; + token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee)); + fndecl = gimple_get_virt_method_for_binfo (token, binfo, &delta, false); + if (!fndecl) + return false; + gcc_assert (integer_zerop (delta)); + gimple_call_set_fndecl (stmt, fndecl); return true; } |