Skip to content

Commit

Permalink
Add function signatures for PHP functions. I've only used them in one…
Browse files Browse the repository at this point in the history
… place so far: adding aliases for actual function parameters only if the call is by reference. Though I can see a number of places this has reduced the number of aliases, the performance results remain the same.

This introduces an optimization Oracle, which sits between PHP and the optimizers. It interfaces with PHP to get function signatures, but can also be given functions signatures by the optimizers.

An MIR::Signature now has an extra field: pass_rest_by_ref, indicating that parameters that do not have a formal parameter are passed by reference. Even though we cant create functions with the property (or I dont think we can), internal functions can use this, and so we need to model it.

We don't need to fix_solo_phi_args since we drop phi args. We can model dataflow properties through phis just as well as if they were put into real statements, but the real statements wouldnt be dropped at the end of the function.
  • Loading branch information
pbiggar committed Nov 18, 2008
1 parent 114d6b5 commit 579c855
Show file tree
Hide file tree
Showing 22 changed files with 225 additions and 44 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Expand Up @@ -174,6 +174,8 @@ libphc_la_SOURCES= \
src/optimize/Lattice.h \
src/optimize/Live_variable_analysis.cpp \
src/optimize/Live_variable_analysis.h \
src/optimize/Oracle.cpp \
src/optimize/Oracle.h \
src/optimize/SCCP.cpp \
src/optimize/SCCP.h \
- src/optimize/Prune_symbol_table.cpp \
Expand Down
36 changes: 23 additions & 13 deletions Makefile.in
Expand Up @@ -102,19 +102,19 @@ am_libphc_la_OBJECTS = getopt1.lo getopt.lo AST_annotate.lo \
Integer.lo Object.lo String.lo Address_taken.lo Basic_block.lo \
CFG.lo CFG_visitor.lo Dead_code_elimination.lo Def_use.lo \
Edge.lo Flow_visitor.lo If_simplification.lo Lattice.lo \
Live_variable_analysis.lo SCCP.lo Prune_symbol_table.lo Set.lo \
Dominance.lo HSSA.lo Phi.lo SSA.lo SSA_ops.lo \
Virtual_variable.lo Visit_once.lo Parse_buffer.lo parse.lo \
PHP_context.lo XML_parser.lo Optimization_pass.lo \
Pass_manager.lo Plugin_pass.lo AST_unparser.lo \
Constant_folding.lo DOT_unparser.lo Invalid_check.lo \
Note_top_level_declarations.lo Process_includes.lo \
Remove_concat_null.lo Remove_parser_temporaries.lo \
Strip_unparser_attributes.lo Token_conversion.lo \
HIR_unparser.lo debug.lo fresh.lo General.lo IR.lo \
PHP_unparser.lo XML_unparser.lo Alias_uppering.lo \
Foreach_uppering.lo Goto_uppering.lo MIR_unparser.lo \
Param_is_ref_uppering.lo
Live_variable_analysis.lo Oracle.lo SCCP.lo \
Prune_symbol_table.lo Set.lo Dominance.lo HSSA.lo Phi.lo \
SSA.lo SSA_ops.lo Virtual_variable.lo Visit_once.lo \
Parse_buffer.lo parse.lo PHP_context.lo XML_parser.lo \
Optimization_pass.lo Pass_manager.lo Plugin_pass.lo \
AST_unparser.lo Constant_folding.lo DOT_unparser.lo \
Invalid_check.lo Note_top_level_declarations.lo \
Process_includes.lo Remove_concat_null.lo \
Remove_parser_temporaries.lo Strip_unparser_attributes.lo \
Token_conversion.lo HIR_unparser.lo debug.lo fresh.lo \
General.lo IR.lo PHP_unparser.lo XML_unparser.lo \
Alias_uppering.lo Foreach_uppering.lo Goto_uppering.lo \
MIR_unparser.lo Param_is_ref_uppering.lo
libphc_la_OBJECTS = $(am_libphc_la_OBJECTS)
plugins_tests_canonical_unparser_la_LIBADD =
am_plugins_tests_canonical_unparser_la_OBJECTS = \
Expand Down Expand Up @@ -712,6 +712,8 @@ libphc_la_SOURCES = \
src/optimize/Lattice.h \
src/optimize/Live_variable_analysis.cpp \
src/optimize/Live_variable_analysis.h \
src/optimize/Oracle.cpp \
src/optimize/Oracle.h \
src/optimize/SCCP.cpp \
src/optimize/SCCP.h \
- src/optimize/Prune_symbol_table.cpp \
Expand Down Expand Up @@ -1314,6 +1316,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Note_top_level_declarations.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Object.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Optimization_pass.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Oracle.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PHP_context.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PHP_unparser.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Param_is_ref_uppering.Plo@am__quote@
Expand Down Expand Up @@ -1829,6 +1832,13 @@ Live_variable_analysis.lo: src/optimize/Live_variable_analysis.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Live_variable_analysis.lo `test -f 'src/optimize/Live_variable_analysis.cpp' || echo '$(srcdir)/'`src/optimize/Live_variable_analysis.cpp

Oracle.lo: src/optimize/Oracle.cpp
@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Oracle.lo -MD -MP -MF $(DEPDIR)/Oracle.Tpo -c -o Oracle.lo `test -f 'src/optimize/Oracle.cpp' || echo '$(srcdir)/'`src/optimize/Oracle.cpp
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/Oracle.Tpo $(DEPDIR)/Oracle.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/optimize/Oracle.cpp' object='Oracle.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Oracle.lo `test -f 'src/optimize/Oracle.cpp' || echo '$(srcdir)/'`src/optimize/Oracle.cpp

SCCP.lo: src/optimize/SCCP.cpp
@am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT SCCP.lo -MD -MP -MF $(DEPDIR)/SCCP.Tpo -c -o SCCP.lo `test -f 'src/optimize/SCCP.cpp' || echo '$(srcdir)/'`src/optimize/SCCP.cpp
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/SCCP.Tpo $(DEPDIR)/SCCP.Plo
Expand Down
4 changes: 2 additions & 2 deletions src/codegen/Generate_C.cpp
Expand Up @@ -439,7 +439,7 @@ class Pattern_method_definition : public Pattern
gen->methods->push_back(signature);

method_entry();
gen->return_by_reference = signature->is_ref;
gen->return_by_reference = signature->return_by_ref;
gen->visit_statement_list(pattern->value->statements);
method_exit();
}
Expand Down Expand Up @@ -2665,7 +2665,7 @@ void Generate_C::post_php_script(PHP_script* in)
// TODO: pass by reference only works for PHP>5.1.0. Do we care?
code
<< "ZEND_BEGIN_ARG_INFO_EX(" << *name << "_arg_info, 0, "
<< (s->is_ref ? "1" : "0")
<< (s->return_by_ref ? "1" : "0")
<< ", 0)\n"
;

Expand Down
1 change: 1 addition & 0 deletions src/codegen/Lift_functions_and_classes.cpp
Expand Up @@ -30,6 +30,7 @@ void Lift_functions_and_classes::children_php_script(PHP_script* in)
new Signature(
new Method_mod(),
false,
false,
new METHOD_NAME(new String("__MAIN__")),
new Formal_parameter_list),
main));
Expand Down
3 changes: 3 additions & 0 deletions src/embed/embed.h
Expand Up @@ -62,8 +62,11 @@ class PHP
static MIR::Literal* fold_string_index (MIR::Literal* array, MIR::Literal* index);

// Functions
// TODO: move to the oracle. We want to keep this to embedding PHP.
static bool is_pure_function (MIR::METHOD_NAME* in);
static MIR::Signature* get_signature (MIR::METHOD_NAME*);
static MIR::Literal* call_function (MIR::METHOD_NAME* in, MIR::Literal_list* params);

};

#endif // PHC_EMBED_H
38 changes: 38 additions & 0 deletions src/embed/optimize.cpp
Expand Up @@ -24,6 +24,8 @@ extern void ***tsrm_ls;

using namespace MIR;



bool
PHP::is_pure_function (METHOD_NAME* in)
{
Expand Down Expand Up @@ -231,6 +233,42 @@ PHP::cast_to (CAST* cast, Literal* lit)
return NULL;
}

Signature*
PHP::get_signature (METHOD_NAME* method_name)
{
zval fn;
INIT_PZVAL (&fn);
ZVAL_STRING (&fn, const_cast<char*> (method_name->value->c_str ()), 0);

zend_fcall_info fci;
zend_fcall_info_cache fcic;
int result = zend_fcall_info_init (&fn, &fci, &fcic TSRMLS_CC);

zend_function* func = fcic.function_handler;
if (result != SUCCESS)
return NULL;

Signature* sig = new Signature (method_name->value->c_str ());

sig->pass_rest_by_ref = func->common.pass_rest_by_reference;
sig->return_by_ref = func->common.return_reference;

for (unsigned int i = 0; i < func->common.num_args; i++)
{
stringstream ss;
ss << "unknown" << i;

Formal_parameter* param =
new Formal_parameter (
NULL, // TODO
ARG_MUST_BE_SENT_BY_REF (func, i+1),
new VARIABLE_NAME (s (ss.str ())));

sig->formal_parameters->push_back (param);
}
return sig;
}


Literal*
PHP::call_function (METHOD_NAME* name, Literal_list* params)
Expand Down
35 changes: 27 additions & 8 deletions src/generated/MIR.cpp
Expand Up @@ -290,18 +290,20 @@ Member::Member()
{
}

Signature::Signature(Method_mod* method_mod, bool is_ref, METHOD_NAME* method_name, Formal_parameter_list* formal_parameters)
Signature::Signature(Method_mod* method_mod, bool pass_rest_by_ref, bool return_by_ref, METHOD_NAME* method_name, Formal_parameter_list* formal_parameters)
{
this->method_mod = method_mod;
this->is_ref = is_ref;
this->pass_rest_by_ref = pass_rest_by_ref;
this->return_by_ref = return_by_ref;
this->method_name = method_name;
this->formal_parameters = formal_parameters;
}

Signature::Signature()
{
this->method_mod = 0;
this->is_ref = 0;
this->pass_rest_by_ref = 0;
this->return_by_ref = 0;
this->method_name = 0;
this->formal_parameters = 0;
}
Expand Down Expand Up @@ -339,7 +341,8 @@ bool Signature::match(Node* in)
else if(!this->method_mod->match(that->method_mod))
return false;

that->is_ref = this->is_ref;
that->pass_rest_by_ref = this->pass_rest_by_ref;
that->return_by_ref = this->return_by_ref;
if(this->method_name == NULL)
{
if(that->method_name != NULL && !that->method_name->match(this->method_name))
Expand Down Expand Up @@ -384,7 +387,10 @@ bool Signature::equals(Node* in)
else if(!this->method_mod->equals(that->method_mod))
return false;

if(this->is_ref != that->is_ref)
if(this->pass_rest_by_ref != that->pass_rest_by_ref)
return false;

if(this->return_by_ref != that->return_by_ref)
return false;

if(this->method_name == NULL || that->method_name == NULL)
Expand Down Expand Up @@ -426,7 +432,8 @@ bool Signature::equals(Node* in)
Signature* Signature::clone()
{
Method_mod* method_mod = this->method_mod ? this->method_mod->clone() : NULL;
bool is_ref = this->is_ref;
bool pass_rest_by_ref = this->pass_rest_by_ref;
bool return_by_ref = this->return_by_ref;
METHOD_NAME* method_name = this->method_name ? this->method_name->clone() : NULL;
Formal_parameter_list* formal_parameters = NULL;
if(this->formal_parameters != NULL)
Expand All @@ -436,7 +443,7 @@ Signature* Signature::clone()
for(i = this->formal_parameters->begin(); i != this->formal_parameters->end(); i++)
formal_parameters->push_back(*i ? (*i)->clone() : NULL);
}
Signature* clone = new Signature(method_mod, is_ref, method_name, formal_parameters);
Signature* clone = new Signature(method_mod, pass_rest_by_ref, return_by_ref, method_name, formal_parameters);
clone->Node::clone_mixin_from(this);
return clone;
}
Expand Down Expand Up @@ -527,12 +534,24 @@ Signature::Signature(const char* name)
{
{
this->method_mod = Method_mod::new_PUBLIC();
this->is_ref = false;
this->pass_rest_by_ref = false;
this->return_by_ref = false;
this->method_name = new METHOD_NAME(name);
this->formal_parameters = new Formal_parameter_list;
}
}

// Returns if the parameter at index PARAM_INDEX is passed by reference.
bool Signature::is_param_passed_by_ref(int param_index)
{
{
if (this->formal_parameters->size () > (unsigned int)(param_index))
return formal_parameters->at (param_index)->is_ref;
else
return this->pass_rest_by_ref;
}
}

Method_mod::Method_mod(bool is_public, bool is_protected, bool is_private, bool is_static, bool is_abstract, bool is_final)
{
this->is_public = is_public;
Expand Down
9 changes: 6 additions & 3 deletions src/generated/MIR.h
Expand Up @@ -339,16 +339,17 @@ class Member : virtual public Node
virtual void assert_valid() = 0;
};

// Signature ::= Method_mod is_ref:"&" METHOD_NAME Formal_parameter* ;
// Signature ::= Method_mod pass_rest_by_ref:"rest_by_ref" return_by_ref:"&" METHOD_NAME Formal_parameter* ;
class Signature : virtual public Node
{
public:
Signature(Method_mod* method_mod, bool is_ref, METHOD_NAME* method_name, Formal_parameter_list* formal_parameters);
Signature(Method_mod* method_mod, bool pass_rest_by_ref, bool return_by_ref, METHOD_NAME* method_name, Formal_parameter_list* formal_parameters);
protected:
Signature();
public:
Method_mod* method_mod;
bool is_ref;
bool pass_rest_by_ref;
bool return_by_ref;
METHOD_NAME* method_name;
Formal_parameter_list* formal_parameters;
public:
Expand All @@ -371,6 +372,8 @@ class Signature : virtual public Node
virtual void assert_valid();
public:
Signature(const char* name);
// Returns if the parameter at index PARAM_INDEX is passed by reference.
bool is_param_passed_by_ref(int param_index);
};

// Method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
Expand Down
5 changes: 3 additions & 2 deletions src/generated/MIR_factory.cpp
Expand Up @@ -57,11 +57,12 @@ Object* Node_factory::create(char const* type_id, List<Object*>* args)
if(!strcmp(type_id, "Signature"))
{
Method_mod* method_mod = dynamic_cast<Method_mod*>(*i++);
bool is_ref = dynamic_cast<Boolean*>(*i++)->value();
bool pass_rest_by_ref = dynamic_cast<Boolean*>(*i++)->value();
bool return_by_ref = dynamic_cast<Boolean*>(*i++)->value();
METHOD_NAME* method_name = dynamic_cast<METHOD_NAME*>(*i++);
Formal_parameter_list* formal_parameters = dynamic_cast<Formal_parameter_list*>(*i++);
assert(i == args->end());
return new Signature(method_mod, is_ref, method_name, formal_parameters);
return new Signature(method_mod, pass_rest_by_ref, return_by_ref, method_name, formal_parameters);
}
if(!strcmp(type_id, "Method_mod"))
{
Expand Down
7 changes: 4 additions & 3 deletions src/generated/MIR_fold.h
Expand Up @@ -214,7 +214,8 @@ class Fold
{
_Method_mod method_mod = 0;
if(in->method_mod != NULL) method_mod = fold_method_mod(in->method_mod);
bool is_ref = in->is_ref;
bool pass_rest_by_ref = in->pass_rest_by_ref;
bool return_by_ref = in->return_by_ref;
_METHOD_NAME method_name = 0;
if(in->method_name != NULL) method_name = fold_method_name(in->method_name);
_List<_Formal_parameter>* formal_parameters = 0;
Expand All @@ -226,7 +227,7 @@ class Fold
if(*i != NULL) formal_parameters->push_back(fold_formal_parameter(*i));
else formal_parameters->push_back(0);
}
return fold_impl_signature(in, method_mod, is_ref, method_name, formal_parameters);
return fold_impl_signature(in, method_mod, pass_rest_by_ref, return_by_ref, method_name, formal_parameters);
}

virtual _Method_mod fold_method_mod(Method_mod* in)
Expand Down Expand Up @@ -764,7 +765,7 @@ class Fold
virtual _Class_mod fold_impl_class_mod(Class_mod* orig, bool is_abstract, bool is_final) { assert(0); };
virtual _Interface_def fold_impl_interface_def(Interface_def* orig, _INTERFACE_NAME interface_name, _List<_INTERFACE_NAME>* extends, _List<_Member>* members) { assert(0); };
virtual _Method fold_impl_method(Method* orig, _Signature signature, _List<_Statement>* statements) { assert(0); };
virtual _Signature fold_impl_signature(Signature* orig, _Method_mod method_mod, bool is_ref, _METHOD_NAME method_name, _List<_Formal_parameter>* formal_parameters) { assert(0); };
virtual _Signature fold_impl_signature(Signature* orig, _Method_mod method_mod, bool pass_rest_by_ref, bool return_by_ref, _METHOD_NAME method_name, _List<_Formal_parameter>* formal_parameters) { assert(0); };
virtual _Method_mod fold_impl_method_mod(Method_mod* orig, bool is_public, bool is_protected, bool is_private, bool is_static, bool is_abstract, bool is_final) { assert(0); };
virtual _Formal_parameter fold_impl_formal_parameter(Formal_parameter* orig, _Type type, bool is_ref, _Name_with_default var) { assert(0); };
virtual _Type fold_impl_type(Type* orig, _CLASS_NAME class_name) { assert(0); };
Expand Down
3 changes: 2 additions & 1 deletion src/generated/MIR_visitor.cpp
Expand Up @@ -756,7 +756,8 @@ void Visitor::children_method(Method* in)
void Visitor::children_signature(Signature* in)
{
visit_method_mod(in->method_mod);
visit_marker("is_ref", in->is_ref);
visit_marker("pass_rest_by_ref", in->pass_rest_by_ref);
visit_marker("return_by_ref", in->return_by_ref);
visit_method_name(in->method_name);
visit_formal_parameter_list(in->formal_parameters);
}
Expand Down
15 changes: 13 additions & 2 deletions src/generated_src/mir.tea
Expand Up @@ -59,7 +59,8 @@ Interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* Member* ;
Member ::= Method | Attribute ;

Method ::= Signature Statement*? ;
Signature ::= Method_mod is_ref:"&"? METHOD_NAME Formal_parameter* ;
-- Pass_rest_by_ref is so that we can treat fully model ZEND_INTERNAL_FUNCTIONs as Signatures
Signature ::= Method_mod pass_rest_by_ref:"rest_by_ref"? return_by_ref:"&"? METHOD_NAME Formal_parameter* ;
Method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
Formal_parameter ::= Type is_ref:"&"? var:Name_with_default ;
Type ::= CLASS_NAME? ;
Expand Down Expand Up @@ -271,10 +272,20 @@ public:
Signature(const char* name)
{
this->method_mod = Method_mod::new_PUBLIC();
this->is_ref = false;
this->pass_rest_by_ref = false;
this->return_by_ref = false;
this->method_name = new METHOD_NAME(name);
this->formal_parameters = new Formal_parameter_list;
}

// Returns if the parameter at index PARAM_INDEX is passed by reference.
bool is_param_passed_by_ref (int param_index)
{
if (this->formal_parameters->size () > (unsigned int)(param_index))
return formal_parameters->at (param_index)->is_ref;
else
return this->pass_rest_by_ref;
}
};

class Method_mod
Expand Down
5 changes: 3 additions & 2 deletions src/hir_to_mir/HIR_to_MIR.h
Expand Up @@ -164,10 +164,11 @@ class HIR_to_MIR : virtual public GC_obj, public HIR::Fold
return result;
}

MIR::Signature* fold_impl_signature(HIR::Signature* orig, MIR::Method_mod* method_mod, bool is_ref, MIR::METHOD_NAME* method_name, MIR::Formal_parameter_list* formal_parameters)
MIR::Signature* fold_impl_signature(HIR::Signature* orig, MIR::Method_mod* method_mod, bool return_by_ref, MIR::METHOD_NAME* method_name, MIR::Formal_parameter_list* formal_parameters)
{
MIR::Signature* result;
result = new MIR::Signature(method_mod, is_ref, method_name, formal_parameters);
// The HIR doesnt have a pass_rest_by_ref field
result = new MIR::Signature(method_mod, false, return_by_ref, method_name, formal_parameters);
copy_attrs (result, orig);
return result;
}
Expand Down

0 comments on commit 579c855

Please sign in to comment.