Skip to content

Commit

Permalink
Add support for unnamed locals
Browse files Browse the repository at this point in the history
Summary:
This wasn't needed until try/finally but here it is. I add a visitor
that walks the CFG and extracts all named locals and unnamed local keys and
then can be used to provide numeric IDs for the unnamed locals

Reviewed By: mofarrell

Differential Revision: D5577679

fbshipit-source-id: 9cc02d1cc0d2b7accd8c67113b91c17547ae59ef
  • Loading branch information
Joseph Griego authored and hhvm-bot committed Aug 16, 2017
1 parent 720bd72 commit 70ae850
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 38 deletions.
97 changes: 97 additions & 0 deletions hphp/php7/analysis.cpp
@@ -0,0 +1,97 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/

#include "hphp/php7/analysis.h"

#include "hphp/util/match.h"

namespace HPHP { namespace php7 {

namespace {

using bc::Local;
using bc::NamedLocal;
using bc::UniqueLocal;

struct LocalSet {
void add(const Local& local) {
match<void>(local,
[&](const NamedLocal& named) {
names.insert(named.name);
},
[&](const UniqueLocal& unique) {
uniqueIds.insert(unique.id);
}
);
}

void allocateUniqueIds() {
uint32_t id = names.size();
for (auto& idPtr : uniqueIds) {
*idPtr = id++;
}
}

std::unordered_set<std::string> names;
std::unordered_set<std::shared_ptr<uint32_t>> uniqueIds;
};

struct LocalsVisitor : CFGVisitor {
explicit LocalsVisitor(LocalSet& locals)
: locals(locals) {}

void beginTry() override {}

void beginCatch() override {}

void endRegion() override {}

void block(Block* blk) override {
for (const auto& bc : blk->code) {
bc.visit(*this);
}
}

template <class T>
void bytecode(const T& bc) {
bc.visit_imms(*this);
}

void imm(Local local) {
locals.add(local);
}

template <class T>
void imm(const T&) {}

LocalSet& locals;
};

} // namespace

std::unordered_set<std::string> analyzeLocals(const Function& func) {
LocalSet locals;
for (const auto& param : func.params) {
locals.add(bc::NamedLocal{param.name});
}
func.cfg.visit(LocalsVisitor(locals));
locals.allocateUniqueIds();
return locals.names;
}

////////////////////////////////////////////////////////////////////////////////

}} // namespace HPHP::php7
31 changes: 31 additions & 0 deletions hphp/php7/analysis.h
@@ -0,0 +1,31 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/

#ifndef incl_HPHP_PHP7_ANALYSIS_H
#define incl_HPHP_PHP7_ANALYSIS_H

#include "hphp/php7/unit.h"

#include <unordered_set>

namespace HPHP { namespace php7 {

/* Find all named locals and allocate IDs for unnamed locals */
std::unordered_set<std::string> analyzeLocals(const Function& func);

}} // namespace HPHP::php7

#endif // incl_HPHP_PHP7_ANALYSIS_H
14 changes: 13 additions & 1 deletion hphp/php7/bytecode.h
Expand Up @@ -44,10 +44,22 @@ struct StringOffsetVector {
};

// to disambiguate between string literals and local names
struct Local {
struct NamedLocal {
std::string name;
};

struct UniqueLocal {
explicit UniqueLocal()
: id(std::make_shared<uint32_t>(-1)) {}

std::shared_ptr<uint32_t> id;
};

typedef boost::variant<
NamedLocal,
UniqueLocal
> Local;

enum MemberType {
Property,
Element,
Expand Down
23 changes: 13 additions & 10 deletions hphp/php7/cfg.cpp
Expand Up @@ -318,16 +318,18 @@ CFG&& CFG::addFinallyGuard(CFG guard) {
// these will be the exits from the finally guard's switch
std::vector<CFG> exits;
auto idx = 0;
UniqueLocal exitCode;

// there's one exit for exceptions, which corresponds to an entry from catch
UniqueLocal exnLocal;
addExnHandler(CFG({
SetL{"exn"},
SetL{exnLocal},
PopC{},
Int{idx++},
SetL{"exit"},
SetL{exitCode},
PopC{}
}).thenJmp(guard.m_entry));
exits.push_back(CFG(CGetL{"exn"}).thenThrow());
exits.push_back(CFG(CGetL{exnLocal}).thenThrow());

// every exit from the region is a linkage, so we intercept them all by
// creating a new trampoline block that jumps to the finally handler
Expand All @@ -340,16 +342,17 @@ CFG&& CFG::addFinallyGuard(CFG guard) {
// for returns, put the relevant value in a local and restore
// it before continuing
[&](const ReturnTarget& ret) {
UniqueLocal tmp;
switch (ret.flavor) {
case Cell:
trampoline->emit(SetL{"return"});
trampoline->emit(SetL{tmp});
trampoline->emit(PopC{});
muxExit.then(CGetL{"return"});
muxExit.then(CGetL{tmp});
break;
case Ref:
trampoline->emit(BindL{"return"});
trampoline->emit(BindL{tmp});
trampoline->emit(PopV{});
muxExit.then(VGetL{"return"});
muxExit.then(VGetL{tmp});
break;
default:
panic("bad return flavor");
Expand All @@ -364,7 +367,7 @@ CFG&& CFG::addFinallyGuard(CFG guard) {
// for all exits, we allocate a code and put this in a local, then jump
// to the finally guard
trampoline->emit(Int{idx++});
trampoline->emit(SetL{"exit"});
trampoline->emit(SetL{exitCode});
trampoline->emit(PopC{});
trampoline->exit(Jmp{guard.m_entry});

Expand All @@ -380,12 +383,12 @@ CFG&& CFG::addFinallyGuard(CFG guard) {
Block* cont = makeBlock();
exits.push_back(CFG().thenJmp(cont));
then(Int{idx++});
then(SetL{"exit"});
then(SetL{exitCode});
then(PopC{});
// execute the finally guard
then(std::move(guard));
// then switch based on the exit code local
then(CGetL{"exit"});
then(CGetL{exitCode});
switchUnbounded(std::move(exits));

return continueFrom(cont);
Expand Down
4 changes: 2 additions & 2 deletions hphp/php7/compiler.cpp
Expand Up @@ -604,7 +604,7 @@ CFG compileGlobalDeclaration(const zend_ast* ast) {
return {
String{name},
VGetG{},
BindL{name},
BindL{NamedLocal{name}},
PopV{}
};
}
Expand All @@ -620,7 +620,7 @@ CFG compileCatch(Function* func, const zend_ast_list* catches) {
auto body = clause->child[2];

auto handler = CFG({
SetL{capture},
SetL{NamedLocal{capture}},
PopC{}
}).then(compileStatement(func, body))
.thenJmp(end);
Expand Down
36 changes: 22 additions & 14 deletions hphp/php7/hhas.cpp
Expand Up @@ -16,6 +16,7 @@

#include "hphp/php7/hhas.h"

#include "hphp/php7/analysis.h"
#include "hphp/php7/cfg.h"
#include "hphp/util/match.h"

Expand All @@ -25,11 +26,12 @@
namespace HPHP { namespace php7 {

namespace {
std::string dump_pseudomain(const Function& func);
std::string dump_function(const Function& func);
std::string dump_blocks(const Function& func);

std::string dump_declvars(const std::unordered_set<std::string>& locals);
std::string dump_pseudomain(const Function& func);
std::string dump_function(const Function& func);
std::string dump_blocks(const Function& func);
std::string dump_declvars(const std::unordered_set<std::string>& locals);

} // namespace

std::string dump_asm(const Unit& unit) {
Expand Down Expand Up @@ -81,7 +83,14 @@ struct InstrVisitor {
}

void imm(const bc::Local& local) {
folly::format(&out, " ${}", local.name);
match<void>(local,
[&](const bc::NamedLocal& named){
folly::format(&out, " ${}", named.name);
},
[&](const bc::UniqueLocal& unique){
folly::format(&out, " _{}", *unique.id);
}
);
}

void imm(const std::vector<Block*>& jmps) {
Expand Down Expand Up @@ -168,7 +177,8 @@ struct InstrVisitor {
},
[&](const LocalMember& m) {
writeType(m.type);
folly::format(&out, "L:${}", m.local.name);
out.append("L:");
imm(m.local);
},
[&](const ImmMember& m) {
writeType(m.type);
Expand Down Expand Up @@ -294,7 +304,7 @@ struct AssemblyVisitor : public boost::static_visitor<void>
std::string dump_pseudomain(const Function& func) {
std::string out;
out.append(".main {\n");
out.append(dump_declvars(func.locals));
out.append(dump_declvars(analyzeLocals(func)));
func.cfg.visit(AssemblyVisitor(out));
out.append("}\n\n");
return out;
Expand All @@ -311,21 +321,19 @@ std::string dump_function(const Function& func) {
param.name);
}
out.append(") {\n");
out.append(dump_declvars(func.locals));
out.append(dump_declvars(analyzeLocals(func)));
func.cfg.visit(AssemblyVisitor(out));
out.append("}\n\n");
return out;
}

std::string dump_declvars(const std::unordered_set<std::string>& locals) {
std::string out;

out.append(" .declvars");
for (const auto& local : locals) {
folly::format(&out, " ${}", local);
out.append(".declvars");
for (const auto& name : locals) {
folly::format(&out, " ${}", name);
}
out.append(";\n");

out.append(";");
return out;
}

Expand Down
18 changes: 9 additions & 9 deletions hphp/php7/lvalue.cpp
Expand Up @@ -199,39 +199,39 @@ struct LocalLvalue : Lvalue {
: name(std::move(name)) {}

CFG getC() override {
return { CGetL{name} };
return { CGetL{NamedLocal{name}} };
}

CFG getV() override {
return { VGetL{name} };
return { VGetL{NamedLocal{name}} };
}

CFG getF(uint32_t slot) override {
return { FPassL{slot, name} };
return { FPassL{slot, NamedLocal{name}} };
}

CFG getB(MinstrSeq& m) override {
m.baseLocal(Local{name});
m.baseLocal(NamedLocal{name});
return {};
}

CFG assign(const zend_ast* rhs) override {
return compileExpression(rhs, Flavor::Cell)
.then(SetL{name});
.then(SetL{NamedLocal{name}});
}

CFG bind(const zend_ast* rhs) override {
return compileExpression(rhs, Flavor::Ref)
.then(BindL{name});
.then(BindL{NamedLocal{name}});
}

CFG assignOp(SetOpOp op, const zend_ast* rhs) override {
return compileExpression(rhs, Flavor::Cell)
.then(SetOpL{name, op});
.then(SetOpL{NamedLocal{name}, op});
}

CFG incDec(IncDecOp op) override {
return { IncDecL{name, op} };
return { IncDecL{NamedLocal{name}, op} };
}

std::string name;
Expand Down Expand Up @@ -558,7 +558,7 @@ std::unique_ptr<Lvalue> Lvalue::getLvalue(const zend_ast* ast) {
auto str = zval_to_string(zend_ast_get_zval(name));
return std::make_unique<DimLvalue>(
getBase(base),
LocalMember{MemberType::Element, Local{str}}
LocalMember{MemberType::Element, NamedLocal{str}}
);
}
}
Expand Down
2 changes: 0 additions & 2 deletions hphp/php7/unit.h
Expand Up @@ -25,7 +25,6 @@

#include <string>
#include <vector>
#include <unordered_set>

namespace HPHP { namespace php7 {

Expand All @@ -51,7 +50,6 @@ struct Function {
Unit* parent;
CFG cfg;
std::vector<Param> params;
std::unordered_set<std::string> locals;
};

struct Unit {
Expand Down

0 comments on commit 70ae850

Please sign in to comment.