Skip to content

Commit

Permalink
[Perf] Improve code generated for runtime type-casting
Browse files Browse the repository at this point in the history
Summary:
This commit attempts to improve the code generated for type casting in
two ways.

First, it cleans up the ambiguous use of expected type in
Expression.cpp- if the type is set, then the type of the expression must
be converted (if necessary) to that type (which is same as the old
meaning). However, if the type is set, it now means that no type
conversion is necessary, where before the meaning was either (1) actual
type and expected type are the same, or (2) no type conversion is
necessary. Doing so allows us to distinguish when we can safely omit a
cast.

Second, it adds a full set of "fast cast" methods to variant, and
adds the necessary code generation to take advantage of the
fast casting method wherever possible.

Test Plan:
slow_tests, inspect www code

Reviewed By: myang
Reviewers: mwilliams, myang, qigao
Commenters: mwilliams
CC: ps, mwilliams, stephentu, myang
Revert Plan:
Tags:

- begin *PUBLIC* platform impact section -
Bugzilla: #
- end platform impact -

Differential Revision: 268015
  • Loading branch information
stephentu authored and macvicar committed Jul 5, 2011
1 parent f7d4e65 commit d7b9fa4
Show file tree
Hide file tree
Showing 21 changed files with 620 additions and 118 deletions.
1 change: 0 additions & 1 deletion src/compiler/analysis/constant_table.cpp
Expand Up @@ -339,7 +339,6 @@ bool ConstantTable::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
cg_printf(isString ? "(" : " = ");
if (value) {
ExpressionPtr exp = dynamic_pointer_cast<Expression>(value);
ASSERT(!exp->getExpectedType());
if (isString && exp->isScalar()) {
ScalarExpressionPtr scalarExp =
dynamic_pointer_cast<ScalarExpression>(exp);
Expand Down
91 changes: 91 additions & 0 deletions src/compiler/analysis/type.cpp
Expand Up @@ -169,6 +169,21 @@ TypePtr Type::Intersection(AnalysisResultConstPtr ar,
return res;
}

bool Type::IsMappedToVariant(TypePtr t) {
switch (t->m_kindOf) {
case KindOfBoolean:
case KindOfInt32 :
case KindOfInt64 :
case KindOfDouble :
case KindOfString :
case KindOfArray :
case KindOfObject :
return false;
default: break;
}
return true;
}

bool Type::IsCastNeeded(AnalysisResultConstPtr ar, TypePtr from, TypePtr to) {
if (SameType(from, to)) return false;
if (!from->m_kindOf) return true;
Expand Down Expand Up @@ -320,6 +335,68 @@ bool Type::IsExactType(KindOf kindOf) {
return kindOf && !(kindOf & (kindOf-1));
}

bool Type::HasFastCastMethod(TypePtr t) {
switch (t->getKindOf()) {
case Type::KindOfBoolean:
case Type::KindOfInt32:
case Type::KindOfInt64:
case Type::KindOfDouble:
case Type::KindOfString:
case Type::KindOfArray:
case Type::KindOfObject:
return true;
default: break;
}
return false;
}

string Type::GetFastCastMethod(
TypePtr dst, bool allowRef, bool forConst) {
const char *prefix0 = allowRef ? "to" : "as";
const char *prefix1 = forConst ? "C" : "";
const char *prefix2 = "Ref";
const char *type;

switch (dst->getKindOf()) {
case Type::KindOfBoolean:
case Type::KindOfInt32:
case Type::KindOfInt64:
case Type::KindOfDouble:
prefix0 = "to";
prefix1 = "";
prefix2 = "Val";
break;
default: break;
}

switch (dst->getKindOf()) {
case Type::KindOfBoolean:
type = "Boolean";
break;
case Type::KindOfInt32:
case Type::KindOfInt64:
type = "Int64";
break;
case Type::KindOfDouble:
type = "Double";
break;
case Type::KindOfString:
type = "Str";
break;
case Type::KindOfArray:
type = "Arr";
break;
case Type::KindOfObject:
type = "Obj";
break;
default:
ASSERT(false);
break;
}

return string(prefix0) + string(prefix1) + string(type) + string(prefix2);
}

/* This new IsLegalCast returns true in a few cases where the old version
* (which was basically a hardcoded truth table) returned false; it seems
* like "true" is in fact the right thing to return. The cases that appear
Expand Down Expand Up @@ -507,6 +584,20 @@ void Type::outputCPPDecl(CodeGenerator &cg, AnalysisResultConstPtr ar,
}
}

void Type::outputCPPFastObjectCast(CodeGenerator &cg,
AnalysisResultConstPtr ar,
BlockScopeRawPtr scope,
bool isConst) {
ASSERT(isSpecificObject());
ClassScopePtr cls(getClass(ar, scope));
ASSERT(cls);
const string &cppClsName = cls->getId(cg);
cg_printf("(%s%s%s&)",
isConst ? "const " : "",
Option::SmartPtrPrefix,
cppClsName.c_str());
}

void Type::outputCPPCast(CodeGenerator &cg, AnalysisResultConstPtr ar,
BlockScopeRawPtr scope) {
switch (m_kindOf) {
Expand Down
21 changes: 21 additions & 0 deletions src/compiler/analysis/type.h
Expand Up @@ -120,6 +120,12 @@ class Type : public JSON::ISerializable {
static TypePtr Intersection(AnalysisResultConstPtr ar,
TypePtr from, TypePtr to);

/**
* Whether or not this type is mapped to type Variant
* in the runtime
*/
static bool IsMappedToVariant(TypePtr t);

/**
* Whether or not a cast is needed during code generation.
*/
Expand Down Expand Up @@ -158,6 +164,15 @@ class Type : public JSON::ISerializable {
*/
static bool IsExactType(KindOf kindOf);

static bool HasFastCastMethod(TypePtr t);

/**
* Returns the name of the method used to fast cast from
* variant to dst
*/
static std::string GetFastCastMethod(
TypePtr dst, bool allowRef, bool forConst);

private:
Type(KindOf kindOf, const std::string &name);

Expand Down Expand Up @@ -198,6 +213,12 @@ class Type : public JSON::ISerializable {
/**
* Generate type conversion in C++.
*/

void outputCPPFastObjectCast(CodeGenerator &cg,
AnalysisResultConstPtr ar,
BlockScopeRawPtr scope,
bool isConst);

void outputCPPCast(CodeGenerator &cg, AnalysisResultConstPtr ar,
BlockScopeRawPtr scope);

Expand Down
27 changes: 26 additions & 1 deletion src/compiler/construct.cpp
Expand Up @@ -17,14 +17,18 @@
#include <compiler/construct.h>
#include <compiler/parser/parser.h>
#include <util/util.h>

#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/ast_walker.h>

#include <compiler/statement/function_statement.h>

#include <compiler/expression/simple_function_call.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/closure_expression.h>
#include <iomanip>

using namespace HPHP;
Expand Down Expand Up @@ -157,6 +161,15 @@ void Construct::printSource(CodeGenerator &cg) {
}

void Construct::dumpNode(int spc, AnalysisResultConstPtr ar) {
dumpNode(spc);
}

void Construct::dumpNode(int spc) const {
// evil, but helpful!
const_cast<Construct*>(this)->dumpNode(spc);
}

void Construct::dumpNode(int spc) {
int nkid = getKidCount();
const char *name = 0;
int type = 0;
Expand Down Expand Up @@ -269,9 +282,13 @@ void Construct::dumpNode(int spc, AnalysisResultConstPtr ar) {
type_info = e->getActualType()->toString();
if (e->getExpectedType()) {
type_info += ":" + e->getExpectedType()->toString();
} else {
type_info += ":";
}
if (e->getImplementedType()) {
type_info += ";" + e->getImplementedType()->toString();
} else {
type_info += ";";
}
type_info = "{" + type_info + "} ";
}
Expand Down Expand Up @@ -362,7 +379,8 @@ void Construct::dumpNode(int spc, AnalysisResultConstPtr ar) {
}
if (objstr != "") objstr = " (" + objstr + ")";

std::cout << type_info << nkid << scontext << sef << localtered << refstr << objstr;
std::cout << type_info << nkid << scontext << sef
<< localtered << refstr << objstr;
if (m_loc) {
std::cout << " " << m_loc->file << ":" <<
m_loc->line1 << "@" << m_loc->char1;
Expand Down Expand Up @@ -399,6 +417,13 @@ class ConstructDumper : public FunctionWalker {
cp->dumpNode(0, m_ar);
}
m_spc -= 2;
// HACK: dump the closure function as a "child" of the
// closure expression
ClosureExpressionPtr c =
boost::dynamic_pointer_cast<ClosureExpression>(cp);
if (c) {
c->getClosureFunction()->dump(m_spc, m_ar);
}
return WalkContinue;
}
private:
Expand Down
10 changes: 7 additions & 3 deletions src/compiler/construct.h
Expand Up @@ -194,17 +194,21 @@ class Construct : public boost::enable_shared_from_this<Construct>,
*/
virtual int getKidCount() const = 0;

void dump(int spc, AnalysisResultPtr ar) {
// helpers for GDB
void dump(int spc, AnalysisResultPtr ar) {
AnalysisResultConstPtr arp(ar);
dump(spc, arp);
}
void dumpNode(int spc, AnalysisResultPtr ar) {
void dumpNode(int spc, AnalysisResultPtr ar) {
AnalysisResultConstPtr arp(ar);
dumpNode(spc, arp);
}
void dumpNode(int spc);
void dumpNode(int spc) const;

void dump(int spc, AnalysisResultConstPtr ar);
void dumpNode(int spc, AnalysisResultConstPtr ar);

static void dump(int spc, AnalysisResultConstPtr ar, bool functionOnly,
const AstWalkerStateVec &start,
ConstructPtr endBefore, ConstructPtr endAfter);
Expand Down Expand Up @@ -254,7 +258,7 @@ class Construct : public boost::enable_shared_from_this<Construct>,
unsigned referenced_valid : 1; // whether or not the above flag is valid
unsigned needed : 1;
unsigned needed_valid : 1; // whether or not the above flag is valid
unsigned chainRoot : 1; // whether this denotes the begining of a
unsigned chainRoot : 1; // whether this denotes the begining of a
// CSE chain
} m_flags;
};
Expand Down
35 changes: 18 additions & 17 deletions src/compiler/expression/array_element_expression.cpp
Expand Up @@ -191,6 +191,18 @@ void ArrayElementExpression::analyzeProgram(AnalysisResultPtr ar) {
}
FunctionScopePtr scope = getFunctionScope();
if (scope) scope->setNeedsCheckMem();
} else {
TypePtr at(m_variable->getActualType());
TypePtr et(m_variable->getExpectedType());
if (et &&
(et->is(Type::KindOfSequence) ||
et->is(Type::KindOfAutoSequence)) &&
at && at->isExactType()) {
// since Sequence maps to Variant in the runtime,
// using Sequence for the expected type will
// never allow the necessary casts to be generated.
m_variable->setExpectedType(at);
}
}
}
}
Expand Down Expand Up @@ -425,7 +437,7 @@ void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
cg_printf(")");
}
} else {
TypePtr type = m_variable->getActualType();
TypePtr type = m_variable->getType();
if (hasContext(UnsetContext)) {
cg_printf("unsetLval(");
m_variable->outputCPP(cg, ar);
Expand All @@ -441,18 +453,7 @@ void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
m_variable->outputCPP(cg, ar);
cg_printf(")");
} else {
TypePtr act;
if (!m_variable->hasCPPTemp() && m_variable->getImplementedType() &&
type->is(Type::KindOfArray) &&
!Type::SameType(m_variable->getImplementedType(), type)) {
act = type;
type = m_variable->getImplementedType();
m_variable->setActualType(m_variable->getImplementedType());
}
m_variable->outputCPP(cg, ar);
if (act) {
m_variable->setActualType(act);
}
}
}
if (m_offset) {
Expand All @@ -468,17 +469,17 @@ void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
TypePtr t;
bool hasCseStore = isRealChainRoot && GetCseTempInfo(
ar,
static_pointer_cast<Expression>(shared_from_this()),
static_pointer_cast<Expression>(shared_from_this()),
t);

if (hasContext(UnsetContext)) {
// do nothing
} else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) {
ASSERT(!isRealChainRoot); // TODO: handle this case
ASSERT(!isRealChainRoot); // TODO: handle this case
cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum);
} else if (m_context & (LValue|RefValue|DeepReference)) {
// if we see an array access element in LValue context, the
// type inference pass will never infer its type to be a string
// type inference pass will never infer its type to be a string
ASSERT(!isStringType);
if (isRealChainRoot && !isArrayType) {
// chain roots for non array types (variants) should call
Expand All @@ -489,7 +490,7 @@ void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
}
lvalAt = true;
} else {
byRef =
byRef =
((m_context & AccessContext) || isRealChainRoot) && !isStringType;
arrRef = byRef && isArrayType;
cg_printf(".rval%s%s(",
Expand All @@ -514,7 +515,7 @@ void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg,
}
} else if (lvalAt) {
if (hasCseStore && !isArrayType) {
cg_printf(", %s%s",
cg_printf(", %s%s",
Option::CseTempStoragePrefix, m_cppCseTemp.c_str());
}
if (hasContext(AccessContext)) {
Expand Down
35 changes: 33 additions & 2 deletions src/compiler/expression/assignment_expression.cpp
Expand Up @@ -314,11 +314,42 @@ static void wrapValue(CodeGenerator &cg, AnalysisResultPtr ar,
void AssignmentExpression::preOutputStash(CodeGenerator &cg,
AnalysisResultPtr ar, int state) {
if (hasCPPTemp()) return;
if (m_value->hasCPPTemp() && Type::SameType(getType(), m_value->getType())) {
if (m_value->hasCPPTemp() &&
(Type::SameType(getType(), m_value->getType()) ||
(Type::IsMappedToVariant(getType()) &&
Type::IsMappedToVariant(m_value->getType())))) {
setUnused(true);
outputCPP(cg, ar);
cg_printf(";\n");
m_cppTemp = m_value->cppTemp();
setCPPTemp(m_value->cppTemp());
return;
}
TypePtr at(getActualType());
TypePtr et(getExpectedType());
TypePtr it(getImplementedType());
if (at && !Type::IsMappedToVariant(at) &&
!et && it && Type::IsMappedToVariant(it)) {
m_value->preOutputStash(cg, ar, state);
if (!m_value->hasCPPTemp()) {
// preOutputStash did no work, so we need to
// explicitly do a stash
TypePtr t(m_value->getType());
bool constRef = !t->isPrimitive() &&
(m_value->isTemporary() || !m_value->isLocalExprAltered());
const string &tmp = m_value->genCPPTemp(cg, ar);
if (constRef) cg_printf("const ");
t->outputCPPDecl(cg, ar, getScope());
const char *ref = constRef ? "&" : "";
cg_printf(" %s%s((", ref, tmp.c_str());
m_value->outputCPP(cg, ar);
cg_printf("));\n");
m_value->setCPPTemp(tmp);
}
ASSERT(m_value->hasCPPTemp());
setUnused(true);
outputCPP(cg, ar);
cg_printf(";\n");
setCPPTemp(m_value->cppTemp());
return;
}
return Expression::preOutputStash(cg, ar, state);
Expand Down

0 comments on commit d7b9fa4

Please sign in to comment.