Difference between revisions of "GCC alwayszero function attribute"
Egmetcalfe (talk | contribs) |
Egmetcalfe (talk | contribs) |
||
(14 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
== Project Description == | == Project Description == | ||
− | This project involves extending gcc to support an | + | This project involves extending gcc to support an attribute for functions which always return zero (see [https://bugzilla.mozilla.org/show_bug.cgi?id=517370 Bug #517370]). This attribute rectifies the fact that GCC cannot determine the results of a call to a virtual member function at compile time (for example). |
== Project Leader(s) == | == Project Leader(s) == | ||
Line 16: | Line 16: | ||
== Project Details == | == Project Details == | ||
− | Relevant blog posts | + | === Relevant blog posts === |
− | + | 10: [http://ehren.wordpress.com/2009/11/24/reworking-my-analysis/ Reworking my analysis] | |
− | |||
− | == | + | 9: [http://ehren.wordpress.com/2009/11/23/build-issues/ Firefox build issues with GCC 4.5] |
+ | |||
+ | 8: [http://ehren.wordpress.com/2009/11/18/analysis-and-type-madness/ Analysis and type madness] | ||
+ | |||
+ | 7: [http://ehren.wordpress.com/2009/11/17/i686-vs-x86-64-issues/ i686 vs x86-64 issues] | ||
+ | |||
+ | 6: [http://ehren.wordpress.com/2009/11/04/creating-a-gcc-optimization-plugin/ Creating a GCC optimization plugin] | ||
+ | |||
+ | 5: [http://ehren.wordpress.com/2009/10/24/a-gcc-hack-my-0-1-release/ A GCC Hack (My 0.1 Release)] | ||
+ | |||
+ | 4: [http://ehren.wordpress.com/2009/10/21/a-gimple-call-flags-mystery/ A Gimple Call Flags Mystery] | ||
+ | |||
+ | 3: [http://ehren.wordpress.com/2009/10/07/adding-a-new-function-attribute-to-gcc/ Adding a new function attribute to GCC] | ||
+ | |||
+ | 2: [http://ehren.wordpress.com/2009/09/30/popping-my-gimples-a-plan/ Popping My GIMPLES (a plan)] | ||
+ | |||
+ | 1: [http://ehren.wordpress.com/2009/09/20/building-gcc-from-trunk/ Building GCC from trunk] | ||
+ | |||
+ | === Breakdown of Releases === | ||
+ | |||
+ | ==== 0.0 ==== | ||
+ | This involved getting acquainted with GCC as well as my initial plan (posts 1-2). | ||
+ | |||
+ | ==== 0.1 ==== | ||
+ | A new attribute and a proof of concept optimization were hacked into GCC (posts 3-5). | ||
+ | |||
+ | ==== 0.2 ==== | ||
+ | The optimization was developed as a separate compiled plugin (post 6). A number of refinements were made to the plugin. Post 7 contains a critical bug fix. Post 8 addresses a minor type safety issue. A Dehydra analysis was introduced in post 8 for identifying alwayszero functions. | ||
+ | |||
+ | ==== ~0.2.5 ==== | ||
+ | A preliminary Treehydra analysis for alwayszero functions based on outparams.js was introduced in post 10. Post 9 details alternative steps required to run the plugin on mozilla-central. | ||
+ | |||
+ | Note: I'd be comfortable including the last two posts in either 0.2 or 0.3. If my 'release style', to be generous, for 0.2 is found lacking, they should perhaps count for 0.2. | ||
+ | |||
+ | == Possible Bug == | ||
+ | |||
+ | Calling gimple_call_flags on a gimple stmt representing an indirect call to a virtual method (with a function attribute) always returns 0. This is even though the correct nonzero value of the call flags can be recognized from other parts of gcc. | ||
+ | |||
+ | For example: | ||
+ | |||
+ | tree-ssa-ccp.c modified with a debugging statement: | ||
+ | <pre> | ||
+ | /* Compute a default value for variable VAR and store it in the | ||
+ | CONST_VAL array. The following rules are used to get default | ||
+ | values: | ||
+ | |||
+ | 1- Global and static variables that are declared constant are | ||
+ | considered CONSTANT. | ||
+ | |||
+ | 2- Any other value is considered UNDEFINED. This is useful when | ||
+ | considering PHI nodes. PHI arguments that are undefined do not | ||
+ | change the constant value of the PHI node, which allows for more | ||
+ | constants to be propagated. | ||
+ | |||
+ | 3- Variables defined by statements other than assignments and PHI | ||
+ | nodes are considered VARYING. | ||
+ | |||
+ | 4- Initial values of variables that are not GIMPLE registers are | ||
+ | considered VARYING. */ | ||
+ | |||
+ | static prop_value_t | ||
+ | get_default_value (tree var) | ||
+ | { | ||
+ | tree sym = SSA_NAME_VAR (var); | ||
+ | prop_value_t val = { UNINITIALIZED, NULL_TREE }; | ||
+ | gimple stmt; | ||
+ | |||
+ | stmt = SSA_NAME_DEF_STMT (var); | ||
+ | |||
+ | |||
+ | |||
+ | /* My debugging statment */ | ||
+ | |||
+ | if (is_gimple_call (stmt)) | ||
+ | fprintf(stderr, "Call flags from tree-ssa-ccp.c: %d\n", | ||
+ | gimple_call_flags (stmt)); | ||
+ | |||
+ | /* End My Debugging Statement */ | ||
+ | |||
+ | |||
+ | if (gimple_nop_p (stmt)) | ||
+ | { | ||
+ | /* Variables defined by an empty statement are those used | ||
+ | before being initialized. If VAR is a local variable, we | ||
+ | can assume initially that it is UNDEFINED, otherwise we must | ||
+ | consider it VARYING. */ | ||
+ | if (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL) | ||
+ | val.lattice_val = UNDEFINED; | ||
+ | else | ||
+ | val.lattice_val = VARYING; | ||
+ | } | ||
+ | else if (is_gimple_assign (stmt) | ||
+ | /* Value-returning GIMPLE_CALL statements assign to | ||
+ | a variable, and are treated similarly to GIMPLE_ASSIGN. */ | ||
+ | || (is_gimple_call (stmt) | ||
+ | && gimple_call_lhs (stmt) != NULL_TREE) | ||
+ | || gimple_code (stmt) == GIMPLE_PHI) | ||
+ | { | ||
+ | tree cst; | ||
+ | if (gimple_assign_single_p (stmt) | ||
+ | && DECL_P (gimple_assign_rhs1 (stmt)) | ||
+ | && (cst = get_symbol_constant_value (gimple_assign_rhs1 (stmt)))) | ||
+ | { | ||
+ | val.lattice_val = CONSTANT; | ||
+ | val.value = cst; | ||
+ | } | ||
+ | else | ||
+ | /* Any other variable defined by an assignment or a PHI node | ||
+ | is considered UNDEFINED. */ | ||
+ | val.lattice_val = UNDEFINED; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | /* Otherwise, VAR will never take on a constant value. */ | ||
+ | val.lattice_val = VARYING; | ||
+ | } | ||
+ | |||
+ | return val; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | tree-optimize.c modified with a debugging statement: | ||
+ | |||
+ | <pre> | ||
+ | unsigned int | ||
+ | execute_fixup_cfg (void) | ||
+ | { | ||
+ | basic_block bb; | ||
+ | gimple_stmt_iterator gsi; | ||
+ | int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0; | ||
+ | |||
+ | if (cfun->eh) | ||
+ | FOR_EACH_BB (bb) | ||
+ | { | ||
+ | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) | ||
+ | { | ||
+ | gimple stmt = gsi_stmt (gsi); | ||
+ | tree decl = is_gimple_call (stmt) | ||
+ | ? gimple_call_fndecl (stmt) | ||
+ | : NULL; | ||
+ | |||
+ | |||
+ | /* My debugging statement */ | ||
+ | if (decl) | ||
+ | fprintf(stderr, "Call flags from tree-optimize.c: %d\n", | ||
+ | gimple_call_flags (stmt)); | ||
+ | /* End my debugging statement */ | ||
+ | |||
+ | |||
+ | if (decl | ||
+ | && gimple_call_flags (stmt) & (ECF_CONST | ||
+ | | ECF_PURE | ||
+ | | ECF_LOOPING_CONST_OR_PURE)) | ||
+ | { | ||
+ | if (gimple_in_ssa_p (cfun)) | ||
+ | { | ||
+ | todo |= TODO_update_ssa | TODO_cleanup_cfg; | ||
+ | mark_symbols_for_renaming (stmt); | ||
+ | update_stmt (stmt); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | maybe_clean_eh_stmt (stmt); | ||
+ | } | ||
+ | |||
+ | if (gimple_purge_dead_eh_edges (bb)) | ||
+ | todo |= TODO_cleanup_cfg; | ||
+ | } | ||
+ | |||
+ | /* Dump a textual representation of the flowgraph. */ | ||
+ | if (dump_file) | ||
+ | gimple_dump_cfg (dump_file, dump_flags); | ||
+ | |||
+ | return todo; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Sample program: | ||
+ | |||
+ | <pre> | ||
+ | /* blah.h */ | ||
+ | class Blah { | ||
+ | public: | ||
+ | virtual int blah() __attribute__((nothrow)); | ||
+ | }; | ||
+ | |||
+ | /* blah.cpp */ | ||
+ | #include "blah.h" | ||
+ | |||
+ | int Blah::blah() | ||
+ | { | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* main.cpp */ | ||
+ | #include "blah.h" | ||
+ | int main() | ||
+ | { | ||
+ | Blah b; | ||
+ | Blah* p = &b; | ||
+ | int x; | ||
+ | x = p->blah(); | ||
+ | return 0; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Compiling sample program with gcc trunk build containing above debugging statements: | ||
+ | <pre> | ||
+ | $ ~/gcc/dist/bin/g++ blah.cpp main.cpp -O2 | ||
+ | Call flags from tree-optimize.c: 64 | ||
+ | Call flags from tree-ssa-ccp.c: 0 | ||
+ | Call flags from tree-optimize.c: 64 | ||
+ | Call flags from tree-optimize.c: 64 | ||
+ | </pre> |
Latest revision as of 10:29, 25 November 2009
Contents
Project Name
GCC alwayszero function attribute
Project Description
This project involves extending gcc to support an attribute for functions which always return zero (see Bug #517370). This attribute rectifies the fact that GCC cannot determine the results of a call to a virtual member function at compile time (for example).
Project Leader(s)
Project Contributor(s)
Project Details
Relevant blog posts
9: Firefox build issues with GCC 4.5
6: Creating a GCC optimization plugin
5: A GCC Hack (My 0.1 Release)
4: A Gimple Call Flags Mystery
3: Adding a new function attribute to GCC
2: Popping My GIMPLES (a plan)
Breakdown of Releases
0.0
This involved getting acquainted with GCC as well as my initial plan (posts 1-2).
0.1
A new attribute and a proof of concept optimization were hacked into GCC (posts 3-5).
0.2
The optimization was developed as a separate compiled plugin (post 6). A number of refinements were made to the plugin. Post 7 contains a critical bug fix. Post 8 addresses a minor type safety issue. A Dehydra analysis was introduced in post 8 for identifying alwayszero functions.
~0.2.5
A preliminary Treehydra analysis for alwayszero functions based on outparams.js was introduced in post 10. Post 9 details alternative steps required to run the plugin on mozilla-central.
Note: I'd be comfortable including the last two posts in either 0.2 or 0.3. If my 'release style', to be generous, for 0.2 is found lacking, they should perhaps count for 0.2.
Possible Bug
Calling gimple_call_flags on a gimple stmt representing an indirect call to a virtual method (with a function attribute) always returns 0. This is even though the correct nonzero value of the call flags can be recognized from other parts of gcc.
For example:
tree-ssa-ccp.c modified with a debugging statement:
/* Compute a default value for variable VAR and store it in the CONST_VAL array. The following rules are used to get default values: 1- Global and static variables that are declared constant are considered CONSTANT. 2- Any other value is considered UNDEFINED. This is useful when considering PHI nodes. PHI arguments that are undefined do not change the constant value of the PHI node, which allows for more constants to be propagated. 3- Variables defined by statements other than assignments and PHI nodes are considered VARYING. 4- Initial values of variables that are not GIMPLE registers are considered VARYING. */ static prop_value_t get_default_value (tree var) { tree sym = SSA_NAME_VAR (var); prop_value_t val = { UNINITIALIZED, NULL_TREE }; gimple stmt; stmt = SSA_NAME_DEF_STMT (var); /* My debugging statment */ if (is_gimple_call (stmt)) fprintf(stderr, "Call flags from tree-ssa-ccp.c: %d\n", gimple_call_flags (stmt)); /* End My Debugging Statement */ if (gimple_nop_p (stmt)) { /* Variables defined by an empty statement are those used before being initialized. If VAR is a local variable, we can assume initially that it is UNDEFINED, otherwise we must consider it VARYING. */ if (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL) val.lattice_val = UNDEFINED; else val.lattice_val = VARYING; } else if (is_gimple_assign (stmt) /* Value-returning GIMPLE_CALL statements assign to a variable, and are treated similarly to GIMPLE_ASSIGN. */ || (is_gimple_call (stmt) && gimple_call_lhs (stmt) != NULL_TREE) || gimple_code (stmt) == GIMPLE_PHI) { tree cst; if (gimple_assign_single_p (stmt) && DECL_P (gimple_assign_rhs1 (stmt)) && (cst = get_symbol_constant_value (gimple_assign_rhs1 (stmt)))) { val.lattice_val = CONSTANT; val.value = cst; } else /* Any other variable defined by an assignment or a PHI node is considered UNDEFINED. */ val.lattice_val = UNDEFINED; } else { /* Otherwise, VAR will never take on a constant value. */ val.lattice_val = VARYING; } return val; }
tree-optimize.c modified with a debugging statement:
unsigned int execute_fixup_cfg (void) { basic_block bb; gimple_stmt_iterator gsi; int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0; if (cfun->eh) FOR_EACH_BB (bb) { for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple stmt = gsi_stmt (gsi); tree decl = is_gimple_call (stmt) ? gimple_call_fndecl (stmt) : NULL; /* My debugging statement */ if (decl) fprintf(stderr, "Call flags from tree-optimize.c: %d\n", gimple_call_flags (stmt)); /* End my debugging statement */ if (decl && gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE)) { if (gimple_in_ssa_p (cfun)) { todo |= TODO_update_ssa | TODO_cleanup_cfg; mark_symbols_for_renaming (stmt); update_stmt (stmt); } } maybe_clean_eh_stmt (stmt); } if (gimple_purge_dead_eh_edges (bb)) todo |= TODO_cleanup_cfg; } /* Dump a textual representation of the flowgraph. */ if (dump_file) gimple_dump_cfg (dump_file, dump_flags); return todo; }
Sample program:
/* blah.h */ class Blah { public: virtual int blah() __attribute__((nothrow)); }; /* blah.cpp */ #include "blah.h" int Blah::blah() { return 0; } /* main.cpp */ #include "blah.h" int main() { Blah b; Blah* p = &b; int x; x = p->blah(); return 0; }
Compiling sample program with gcc trunk build containing above debugging statements:
$ ~/gcc/dist/bin/g++ blah.cpp main.cpp -O2 Call flags from tree-optimize.c: 64 Call flags from tree-ssa-ccp.c: 0 Call flags from tree-optimize.c: 64 Call flags from tree-optimize.c: 64