Comparar commits
9 Commits
master
...
HHVM-2.3.0
| Autor | SHA1 | Data | |
|---|---|---|---|
| 3fa32fab6e | |||
| 075cb79c9f | |||
| a1f2c80031 | |||
| 4e791cc4b6 | |||
| e62e500a84 | |||
| bab7343e83 | |||
| ef3d8e9fc5 | |||
| 9401291363 | |||
| f0665f2e92 |
@@ -4251,7 +4251,9 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
|
||||
e.AKExists();
|
||||
return true;
|
||||
}
|
||||
} else if (call->isCallToFunction("idx") && call->isOptimizable()) {
|
||||
} else if (call->isCallToFunction("idx") &&
|
||||
call->isOptimizable() &&
|
||||
!Option::JitEnableRenameFunction) {
|
||||
if (params && (params->getCount() == 2 || params->getCount() == 3)) {
|
||||
visit((*params)[0]);
|
||||
emitConvertToCell(e);
|
||||
|
||||
@@ -70,7 +70,7 @@ const StaticString
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef smart::unique_ptr<CufIter>::type SmartCufIterPtr;
|
||||
typedef smart::unique_ptr<CufIter> SmartCufIterPtr;
|
||||
|
||||
bool array_is_valid_callback(CArrRef arr) {
|
||||
if (arr.size() != 2 || !arr.exists(int64_t(0)) || !arr.exists(int64_t(1))) {
|
||||
|
||||
@@ -339,13 +339,13 @@ class AutoloadHandler : public RequestEventHandler {
|
||||
struct HandlerBundle {
|
||||
HandlerBundle() = delete;
|
||||
HandlerBundle(CVarRef handler,
|
||||
smart::unique_ptr<CufIter>::type& cufIter) :
|
||||
smart::unique_ptr<CufIter>& cufIter) :
|
||||
m_handler(handler) {
|
||||
m_cufIter = std::move(cufIter);
|
||||
}
|
||||
|
||||
Variant m_handler; // used to respond to f_spl_autoload_functions
|
||||
smart::unique_ptr<CufIter>::type m_cufIter; // used to invoke handlers
|
||||
smart::unique_ptr<CufIter> m_cufIter; // used to invoke handlers
|
||||
};
|
||||
|
||||
class CompareBundles {
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "hphp/runtime/base/simple-counter.h"
|
||||
#include "hphp/runtime/base/extended-logger.h"
|
||||
#include "hphp/runtime/base/stream-wrapper-registry.h"
|
||||
#include "hphp/runtime/vm/debug/debug.h"
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
@@ -624,6 +625,7 @@ hugifyText(char* from, char* to) {
|
||||
mprotect(from, sz, PROT_READ | PROT_EXEC);
|
||||
free(mem);
|
||||
mlock(from, to - from);
|
||||
Debug::DebugInfo::setPidMapOverlay(from, to);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -110,24 +110,18 @@ struct Allocator {
|
||||
};
|
||||
|
||||
/*
|
||||
* Shorthand to create a std::unique_ptr to a smart-allocated object.
|
||||
* Shorthand to create a smart::unique_ptr to a smart-allocated object.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* auto ptr = smart::make_unique<Foo>(arg1, arg2);
|
||||
*
|
||||
* If you need to make a typedef to one, since we don't have type
|
||||
* aliases yet in our version of gcc you have to do:
|
||||
*
|
||||
* typedef smart::unique_ptr<T>::type type;
|
||||
*/
|
||||
|
||||
template<class T> struct unique_ptr
|
||||
: folly::AllocatorUniquePtr<T,Allocator<T>>
|
||||
{};
|
||||
template<class T>
|
||||
using unique_ptr = typename folly::AllocatorUniquePtr<T,Allocator<T>>::type;
|
||||
|
||||
template<class T, class... Args>
|
||||
typename unique_ptr<T>::type make_unique(Args&&... args) {
|
||||
unique_ptr<T> make_unique(Args&&... args) {
|
||||
return folly::allocate_unique<T>(
|
||||
Allocator<T>(),
|
||||
std::forward<Args>(args)...
|
||||
@@ -136,7 +130,7 @@ typename unique_ptr<T>::type make_unique(Args&&... args) {
|
||||
|
||||
#ifndef __APPLE__ // XXX: this affects codegen quality but not correctness
|
||||
static_assert(
|
||||
sizeof(unique_ptr<int>::type) == sizeof(std::unique_ptr<int>),
|
||||
sizeof(unique_ptr<int>) == sizeof(std::unique_ptr<int>),
|
||||
"smart::unique_ptr pointer should not be larger than std::unique_ptr"
|
||||
);
|
||||
#endif
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "hphp/runtime/ext/thrift/transport.h"
|
||||
#include "hphp/runtime/ext/ext_thrift.h"
|
||||
#include "hphp/runtime/ext/ext_class.h"
|
||||
#include "hphp/runtime/ext/ext_collections.h"
|
||||
#include "hphp/runtime/ext/ext_reflection.h"
|
||||
#include "hphp/runtime/base/base-includes.h"
|
||||
#include "hphp/util/logger.h"
|
||||
@@ -44,8 +43,6 @@ StaticString PHPTransport::s_type("type");
|
||||
StaticString PHPTransport::s_ktype("ktype");
|
||||
StaticString PHPTransport::s_vtype("vtype");
|
||||
StaticString PHPTransport::s_etype("etype");
|
||||
StaticString PHPTransport::s_format("format");
|
||||
StaticString PHPTransport::s_collection("collection");
|
||||
|
||||
|
||||
IMPLEMENT_DEFAULT_EXTENSION(thrift_protocol);
|
||||
@@ -189,13 +186,7 @@ Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport,
|
||||
AccessFlags::Error_Key).toArray();
|
||||
Array valspec = fieldspec.rvalAt(PHPTransport::s_val,
|
||||
AccessFlags::Error_Key).toArray();
|
||||
String format = fieldspec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
ret = NEWOBJ(c_Map)();
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
}
|
||||
ret = Array::Create();
|
||||
|
||||
for (uint32_t s = 0; s < size; ++s) {
|
||||
Variant key = binary_deserialize(types[0], transport, keyspec);
|
||||
@@ -210,13 +201,7 @@ Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport,
|
||||
Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem,
|
||||
AccessFlags::Error_Key);
|
||||
Array elemspec = elemvar.toArray();
|
||||
String format = fieldspec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
ret = NEWOBJ(c_Vector)();
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
}
|
||||
ret = Array::Create();
|
||||
|
||||
for (uint32_t s = 0; s < size; ++s) {
|
||||
Variant value = binary_deserialize(type, transport, elemspec);
|
||||
@@ -233,33 +218,15 @@ Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport,
|
||||
Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem,
|
||||
AccessFlags::Error_Key);
|
||||
Array elemspec = elemvar.toArray();
|
||||
String format = fieldspec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
p_Set set_ret = NEWOBJ(c_Set)();
|
||||
ret = Array::Create();
|
||||
|
||||
for (uint32_t s = 0; s < size; ++s) {
|
||||
Variant key = binary_deserialize(type, transport, elemspec);
|
||||
for (uint32_t s = 0; s < size; ++s) {
|
||||
Variant key = binary_deserialize(type, transport, elemspec);
|
||||
|
||||
if (key.isInteger()) {
|
||||
set_ret->t_add(key);
|
||||
} else {
|
||||
set_ret->t_add(key.toString());
|
||||
}
|
||||
}
|
||||
|
||||
ret = Variant(set_ret);
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
|
||||
for (uint32_t s = 0; s < size; ++s) {
|
||||
Variant key = binary_deserialize(type, transport, elemspec);
|
||||
|
||||
if (key.isInteger()) {
|
||||
ret.set(key, true);
|
||||
} else {
|
||||
ret.set(key.toString(), true);
|
||||
}
|
||||
if (key.isInteger()) {
|
||||
ret.set(key, true);
|
||||
} else {
|
||||
ret.set(key.toString(), true);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "hphp/runtime/base/request-local.h"
|
||||
#include "hphp/runtime/ext/thrift/transport.h"
|
||||
#include "hphp/runtime/ext/ext_collections.h"
|
||||
#include "hphp/runtime/ext/ext_reflection.h"
|
||||
#include "hphp/runtime/ext/ext_thrift.h"
|
||||
|
||||
@@ -710,10 +709,10 @@ class CompactReader {
|
||||
return readMap(spec);
|
||||
|
||||
case T_LIST:
|
||||
return readList(spec);
|
||||
return readList(spec, C_LIST_LIST);
|
||||
|
||||
case T_SET:
|
||||
return readSet(spec);
|
||||
return readList(spec, C_LIST_SET);
|
||||
|
||||
default:
|
||||
throw InvalidArgumentException("unknown TType", type);
|
||||
@@ -824,14 +823,7 @@ class CompactReader {
|
||||
AccessFlags::Error).toArray();
|
||||
Array valueSpec = spec.rvalAt(PHPTransport::s_val,
|
||||
AccessFlags::Error).toArray();
|
||||
String format = spec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
Variant ret;
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
ret = NEWOBJ(c_Map)();
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
}
|
||||
Variant ret = Array::Create();
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
Variant key = readField(keySpec, keyType);
|
||||
@@ -843,60 +835,24 @@ class CompactReader {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant readList(CArrRef spec) {
|
||||
Variant readList(CArrRef spec, CListType listType) {
|
||||
TType valueType;
|
||||
uint32_t size;
|
||||
readListBegin(valueType, size);
|
||||
|
||||
Array valueSpec = spec.rvalAt(PHPTransport::s_elem,
|
||||
AccessFlags::Error_Key).toArray();
|
||||
String format = spec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
Variant ret;
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
ret = NEWOBJ(c_Vector)();
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
}
|
||||
Variant ret = Array::Create();
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
Variant value = readField(valueSpec, valueType);
|
||||
ret.append(value);
|
||||
}
|
||||
|
||||
readCollectionEnd();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant readSet(CArrRef spec) {
|
||||
TType valueType;
|
||||
uint32_t size;
|
||||
readListBegin(valueType, size);
|
||||
|
||||
Array valueSpec = spec.rvalAt(PHPTransport::s_elem,
|
||||
AccessFlags::Error_Key).toArray();
|
||||
String format = spec.rvalAt(PHPTransport::s_format,
|
||||
AccessFlags::None).toString();
|
||||
Variant ret;
|
||||
if (format.equal(PHPTransport::s_collection)) {
|
||||
p_Set set_ret = NEWOBJ(c_Set)();
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
Variant value = readField(valueSpec, valueType);
|
||||
set_ret->t_add(value);
|
||||
}
|
||||
|
||||
ret = Variant(set_ret);
|
||||
} else {
|
||||
ret = Array::Create();
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
Variant value = readField(valueSpec, valueType);
|
||||
if (listType == C_LIST_LIST) {
|
||||
ret.append(value);
|
||||
} else {
|
||||
ret.set(value, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readCollectionEnd();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,6 @@ public:
|
||||
static StaticString s_ktype;
|
||||
static StaticString s_vtype;
|
||||
static StaticString s_etype;
|
||||
static StaticString s_format;
|
||||
static StaticString s_collection;
|
||||
|
||||
public:
|
||||
Object protocol() { return p; }
|
||||
|
||||
@@ -16,22 +16,28 @@
|
||||
|
||||
#include "hphp/runtime/vm/debug/debug.h"
|
||||
#include "hphp/runtime/vm/debug/gdb-jit.h"
|
||||
#include "hphp/runtime/vm/jit/translator-x64.h"
|
||||
|
||||
#include "hphp/runtime/base/execution-context.h"
|
||||
|
||||
#include "hphp/util/current-executable.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "hphp/runtime/vm/jit/translator-x64.h"
|
||||
#include <bfd.h>
|
||||
|
||||
using namespace HPHP::Transl;
|
||||
|
||||
namespace HPHP {
|
||||
namespace Debug {
|
||||
|
||||
void* DebugInfo::pidMapOverlayStart;
|
||||
void* DebugInfo::pidMapOverlayEnd;
|
||||
|
||||
DebugInfo* DebugInfo::Get() {
|
||||
return tx64->getDebugInfo();
|
||||
}
|
||||
@@ -41,6 +47,7 @@ DebugInfo::DebugInfo() {
|
||||
sizeof m_perfMapName,
|
||||
"/tmp/perf-%d.map", getpid());
|
||||
m_perfMap = fopen(m_perfMapName, "w");
|
||||
generatePidMapOverlay();
|
||||
}
|
||||
|
||||
DebugInfo::~DebugInfo() {
|
||||
@@ -50,6 +57,74 @@ DebugInfo::~DebugInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfo::generatePidMapOverlay() {
|
||||
if (!m_perfMap || !pidMapOverlayStart) return;
|
||||
|
||||
std::string self = current_executable_path();
|
||||
bfd* abfd = bfd_openr(self.c_str(), nullptr);
|
||||
#ifdef BFD_DECOMPRESS
|
||||
abfd->flags |= BFD_DECOMPRESS;
|
||||
#endif
|
||||
char **match = nullptr;
|
||||
if (!bfd_check_format(abfd, bfd_archive) &&
|
||||
bfd_check_format_matches(abfd, bfd_object, &match)) {
|
||||
|
||||
std::vector<asymbol*> sorted;
|
||||
long storage_needed = bfd_get_symtab_upper_bound (abfd);
|
||||
|
||||
if (storage_needed <= 0) return;
|
||||
|
||||
auto symbol_table = (asymbol**)malloc(storage_needed);
|
||||
|
||||
long number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table);
|
||||
|
||||
for (long i = 0; i < number_of_symbols; i++) {
|
||||
auto sym = symbol_table[i];
|
||||
if (!(sym->flags & (BSF_LOCAL|BSF_GLOBAL))) continue;
|
||||
if (sym->flags &
|
||||
(BSF_INDIRECT|BSF_SECTION_SYM|BSF_WEAK|BSF_FILE|BSF_OBJECT)) {
|
||||
continue;
|
||||
}
|
||||
auto sec = sym->section;
|
||||
if (!(sec->flags & (SEC_ALLOC|SEC_LOAD|SEC_CODE))) continue;
|
||||
auto addr = sec->vma + sym->value;
|
||||
if (addr < uintptr_t(pidMapOverlayStart) ||
|
||||
addr >= uintptr_t(pidMapOverlayEnd)) {
|
||||
continue;
|
||||
}
|
||||
sorted.push_back(sym);
|
||||
}
|
||||
|
||||
std::sort(sorted.begin(), sorted.end(), [](asymbol* a, asymbol* b) {
|
||||
auto addra = a->section->vma + a->value;
|
||||
auto addrb = b->section->vma + b->value;
|
||||
if (addra != addrb) return addra < addrb;
|
||||
return strncmp("_ZN4HPHP", a->name, 8) &&
|
||||
!strncmp("_ZN4HPHP", b->name, 8);
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < sorted.size(); i++) {
|
||||
auto sym = sorted[i];
|
||||
auto addr = sym->section->vma + sym->value;
|
||||
unsigned size;
|
||||
if (i + 1 < sorted.size()) {
|
||||
auto s2 = sorted[i + 1];
|
||||
size = s2->section->vma + s2->value - addr;
|
||||
} else {
|
||||
size = uintptr_t(pidMapOverlayEnd) - addr;
|
||||
}
|
||||
if (!size) continue;
|
||||
fprintf(m_perfMap, "%lx %x %s\n",
|
||||
long(addr), size, sym->name);
|
||||
}
|
||||
|
||||
free(symbol_table);
|
||||
free(match);
|
||||
}
|
||||
bfd_close(abfd);
|
||||
return;
|
||||
}
|
||||
|
||||
void DebugInfo::recordStub(TCRange range, const char* name) {
|
||||
if (range.isAstubs()) {
|
||||
m_astubsDwarfInfo.addTracelet(range, name, nullptr, nullptr, false, false);
|
||||
|
||||
@@ -41,8 +41,13 @@ class DebugInfo {
|
||||
|
||||
void debugSync();
|
||||
static DebugInfo* Get();
|
||||
|
||||
static void setPidMapOverlay(void* from, void* to) {
|
||||
pidMapOverlayStart = from;
|
||||
pidMapOverlayEnd = to;
|
||||
}
|
||||
private:
|
||||
void generatePidMapOverlay();
|
||||
|
||||
/* maintain separate dwarf info for a and astubs, so that we
|
||||
* don't emit dwarf info for the two in the same ELF file.
|
||||
* gdb tends to get confused when it sees dwarf info for
|
||||
@@ -56,6 +61,9 @@ class DebugInfo {
|
||||
*/
|
||||
FILE* m_perfMap;
|
||||
char m_perfMapName[64];
|
||||
|
||||
static void* pidMapOverlayStart;
|
||||
static void* pidMapOverlayEnd;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -178,11 +178,19 @@ void forPreorderDoms(Block* block, const DomChildren& children,
|
||||
}
|
||||
}
|
||||
|
||||
template <class Body>
|
||||
void forEachTrace(IRUnit& unit, Body body) {
|
||||
body(unit.main());
|
||||
for (auto& exit : unit.exits()) {
|
||||
body(exit.get());
|
||||
}
|
||||
}
|
||||
|
||||
template <class Body>
|
||||
void forEachTrace(const IRUnit& unit, Body body) {
|
||||
body(unit.main());
|
||||
for (auto exit : unit.exits()) {
|
||||
body(exit);
|
||||
for (auto& exit : unit.exits()) {
|
||||
body(exit.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +199,7 @@ void forEachTraceBlock(const IRUnit& unit, Body body) {
|
||||
for (auto block : unit.main()->blocks()) {
|
||||
body(block);
|
||||
}
|
||||
for (auto exit : unit.exits()) {
|
||||
for (auto& exit : unit.exits()) {
|
||||
for (auto block : exit->blocks()) {
|
||||
body(block);
|
||||
}
|
||||
@@ -208,7 +216,7 @@ void forEachInst(const BlockList& blocks, Body body) {
|
||||
}
|
||||
|
||||
template <class Body>
|
||||
void forEachTraceInst(const IRUnit& unit, Body body) {
|
||||
void forEachTraceInst(IRUnit& unit, Body body) {
|
||||
forEachTrace(unit, [=](IRTrace* t) {
|
||||
forEachInst(t->blocks(), body);
|
||||
});
|
||||
|
||||
@@ -438,9 +438,12 @@ void optimizeActRecs(BlockList& blocks, DceState& state, IRUnit& unit,
|
||||
|
||||
void eliminateDeadCode(IRUnit& unit) {
|
||||
auto removeEmptyExitTraces = [&] {
|
||||
unit.exits().remove_if([](IRTrace* exit) {
|
||||
auto& exits = unit.exits();
|
||||
auto isEmpty = [](smart::unique_ptr<IRTrace>& exit) {
|
||||
return exit->blocks().empty();
|
||||
});
|
||||
};
|
||||
exits.erase(std::remove_if(exits.begin(), exits.end(), isEmpty),
|
||||
exits.end());
|
||||
};
|
||||
|
||||
// kill unreachable code and remove any traces that are now empty
|
||||
|
||||
@@ -69,7 +69,8 @@ bool classIsUniqueInterface(const Class* cls) {
|
||||
HhbcTranslator::HhbcTranslator(Offset startOffset,
|
||||
uint32_t initialSpOffsetFromFp,
|
||||
const Func* func)
|
||||
: m_tb(new TraceBuilder(startOffset,
|
||||
: m_unit(startOffset)
|
||||
, m_tb(new TraceBuilder(startOffset,
|
||||
initialSpOffsetFromFp,
|
||||
m_unit,
|
||||
func))
|
||||
|
||||
@@ -22,6 +22,15 @@ namespace HPHP { namespace JIT {
|
||||
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
IRUnit::IRUnit(Offset initialBcOffset)
|
||||
: m_nextBlockId(0)
|
||||
, m_nextOpndId(0)
|
||||
, m_nextInstId(0)
|
||||
, m_bcOff(initialBcOffset)
|
||||
, m_main(smart::make_unique<IRTrace>(*this, defBlock()))
|
||||
{
|
||||
}
|
||||
|
||||
IRInstruction* IRUnit::defLabel(unsigned numDst, BCMarker marker) {
|
||||
IRInstruction inst(DefLabel, marker);
|
||||
IRInstruction* label = cloneInstruction(&inst);
|
||||
@@ -58,18 +67,10 @@ SSATmp* IRUnit::findConst(ConstData& cdata, Type ctype) {
|
||||
return m_constTable.insert(cloneInstruction(&inst)->dst());
|
||||
}
|
||||
|
||||
Block* IRUnit::makeMain(uint32_t bcOff) {
|
||||
assert(!m_main);
|
||||
auto entry = defBlock();
|
||||
m_bcOff = bcOff;
|
||||
m_main = new (m_arena) IRTrace(*this, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
Block* IRUnit::addExit() {
|
||||
auto exit = defBlock();
|
||||
exit->setHint(Block::Hint::Unlikely);
|
||||
m_exits.push_back(new (m_arena) IRTrace(*this, exit));
|
||||
m_exits.push_back(smart::make_unique<IRTrace>(*this, exit));
|
||||
return exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "folly/ScopeGuard.h"
|
||||
#include "hphp/util/arena.h"
|
||||
#include "hphp/runtime/vm/jit/ir.h"
|
||||
#include "hphp/runtime/vm/jit/ir-trace.h"
|
||||
#include "hphp/runtime/vm/jit/cse.h"
|
||||
#include "hphp/runtime/base/memory-manager.h"
|
||||
|
||||
@@ -168,12 +169,7 @@ class IRUnit {
|
||||
TRACE_SET_MOD(hhir);
|
||||
|
||||
public:
|
||||
IRUnit()
|
||||
: m_nextBlockId(0)
|
||||
, m_nextOpndId(0)
|
||||
, m_nextInstId(0)
|
||||
, m_main(nullptr)
|
||||
{}
|
||||
explicit IRUnit(Offset initialBcOffset);
|
||||
|
||||
/*
|
||||
* Create an IRInstruction with lifetime equivalent to this IRUnit.
|
||||
@@ -263,9 +259,8 @@ public:
|
||||
IRInstruction* mov(SSATmp* dst, SSATmp* src, BCMarker marker);
|
||||
|
||||
/*
|
||||
* Create a new trace
|
||||
* Create a new exit trace.
|
||||
*/
|
||||
Block* makeMain(uint32_t bcOff);
|
||||
Block* addExit();
|
||||
|
||||
Arena& arena() { return m_arena; }
|
||||
@@ -273,7 +268,8 @@ public:
|
||||
uint32_t numBlocks() const { return m_nextBlockId; }
|
||||
uint32_t numInsts() const { return m_nextInstId; }
|
||||
CSEHash& constTable() { return m_constTable; }
|
||||
IRTrace* main() const { return m_main; }
|
||||
IRTrace* main() { return m_main.get(); }
|
||||
const IRTrace* main() const { return m_main.get(); }
|
||||
uint32_t bcOff() const { return m_bcOff; }
|
||||
|
||||
// Overloads useful for StateVector and IdSet
|
||||
@@ -281,8 +277,8 @@ public:
|
||||
uint32_t numIds(const Block*) const { return numBlocks(); }
|
||||
uint32_t numIds(const IRInstruction*) const { return numInsts(); }
|
||||
|
||||
typedef std::list<IRTrace*> ExitList;
|
||||
ExitList& exits() { return m_exits; }
|
||||
typedef smart::vector<smart::unique_ptr<IRTrace>> ExitList;
|
||||
ExitList& exits() { return m_exits; }
|
||||
const ExitList& exits() const { return m_exits; }
|
||||
Block* entry() const;
|
||||
std::string toString() const;
|
||||
@@ -296,13 +292,13 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
Arena m_arena; // contains IRTrace, Block, IRInstruction, and SSATmp objects
|
||||
Arena m_arena; // contains Block, IRInstruction, and SSATmp objects
|
||||
CSEHash m_constTable; // DefConst's for each unique constant in this IR
|
||||
uint32_t m_nextBlockId;
|
||||
uint32_t m_nextOpndId;
|
||||
uint32_t m_nextInstId;
|
||||
uint32_t m_bcOff; // bytecode offset where this unit starts
|
||||
IRTrace* m_main; // main entry point trace
|
||||
smart::unique_ptr<IRTrace> m_main; // main entry point trace
|
||||
ExitList m_exits; // exit traces
|
||||
};
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ class NormalizedInstruction {
|
||||
}
|
||||
|
||||
private:
|
||||
smart::vector<smart::unique_ptr<DynLocation>::type> m_dynLocs;
|
||||
smart::vector<smart::unique_ptr<DynLocation>> m_dynLocs;
|
||||
};
|
||||
|
||||
} } // HPHP::Transl
|
||||
|
||||
@@ -353,7 +353,7 @@ static BlockList blocks(const IRUnit& unit,
|
||||
smart::vector<Block*> blocks;
|
||||
blocks.assign(trace->blocks().begin(), trace->blocks().end());
|
||||
if (trace->isMain()) {
|
||||
for (auto exit : unit.exits()) {
|
||||
for (auto& exit : unit.exits()) {
|
||||
blocks.insert(blocks.end(), exit->blocks().begin(), exit->blocks().end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,11 +454,15 @@ void pmethodCacheMissPath(MethodCache* mce,
|
||||
LeaseHolder writer(Translator::WriteLease());
|
||||
if (!writer) return;
|
||||
|
||||
auto smashMov = [&] (TCA addr, uintptr_t value) {
|
||||
auto smashMov = [&] (TCA addr, uintptr_t value) -> bool {
|
||||
always_assert(isSmashable(addr + 2, 8));
|
||||
assert(addr[0] == 0x49 && addr[1] == 0xba);
|
||||
auto const ptr = reinterpret_cast<uintptr_t*>(addr + 2);
|
||||
if (!(*ptr & 1)) {
|
||||
return false;
|
||||
}
|
||||
*ptr = value;
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -495,12 +499,14 @@ void pmethodCacheMissPath(MethodCache* mce,
|
||||
fval < std::numeric_limits<uint32_t>::max() &&
|
||||
cval < std::numeric_limits<uint32_t>::max();
|
||||
|
||||
uintptr_t imm = 0x2; /* not a Class, but clear low bit */
|
||||
if (cacheable) {
|
||||
assert(!(mce->m_value->attrs() & AttrStatic));
|
||||
auto const imm = fval << 32 | mce->m_key;
|
||||
smashMov(pdata->smashImmAddr, imm);
|
||||
} else {
|
||||
smashMov(pdata->smashImmAddr, 0x2 /* not a Class, but clear low bit */);
|
||||
imm = fval << 32 | cval;
|
||||
}
|
||||
if (!smashMov(pdata->smashImmAddr, imm)) {
|
||||
// someone beat us to it
|
||||
return methodCacheSlowerPath<Fatal>(mce, ar, name, cls);
|
||||
}
|
||||
|
||||
// Regardless of whether the inline cache was populated, smash the
|
||||
|
||||
@@ -35,7 +35,7 @@ TraceBuilder::TraceBuilder(Offset initialBcOffset,
|
||||
: m_unit(unit)
|
||||
, m_simplifier(*this)
|
||||
, m_state(unit, initialSpOffsetFromFp, func)
|
||||
, m_curTrace(m_unit.makeMain(initialBcOffset)->trace())
|
||||
, m_curTrace(m_unit.main())
|
||||
, m_curBlock(nullptr)
|
||||
, m_enableSimplification(false)
|
||||
, m_inReoptimize(false)
|
||||
|
||||
@@ -3170,6 +3170,20 @@ bool shouldAnalyzeCallee(const NormalizedInstruction* fcall,
|
||||
// inline if there are any calls in order to prepare arguments.
|
||||
for (auto* ni = fcall->prev; ni; ni = ni->prev) {
|
||||
if (ni->source.offset() == fpi->m_fpushOff) {
|
||||
if (ni->op() == OpFPushObjMethodD ||
|
||||
ni->op() == OpFPushObjMethod) {
|
||||
if (!ni->inputs[ni->op() == OpFPushObjMethod]->isObject()) {
|
||||
/*
|
||||
* In this case, we're going to throw or fatal when we
|
||||
* execute the FPush*. But we have statically proven that
|
||||
* if we get to the FCall, then target is the Func that will
|
||||
* be called. So the FCall is unreachable - but unfortunately,
|
||||
* various assumptions by the jit will be violated if we try
|
||||
* to inline it. So just don't inline in that case.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (isFCallStar(ni->op()) || ni->op() == OpFCallBuiltin) {
|
||||
|
||||
@@ -1817,11 +1817,11 @@
|
||||
},
|
||||
{
|
||||
"name": "HPHP_VERSION",
|
||||
"value": "2.3.0-dev"
|
||||
"value": "2.3.0"
|
||||
},
|
||||
{
|
||||
"name": "HHVM_VERSION",
|
||||
"value": "2.3.0-dev"
|
||||
"value": "2.3.0"
|
||||
},
|
||||
{
|
||||
"name": "HTML_ENTITIES",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?hh ;
|
||||
|
||||
class X {
|
||||
private $foo;
|
||||
|
||||
function __construct($a) {
|
||||
$this->foo = $a;
|
||||
}
|
||||
|
||||
function foo() { return $this->foo; }
|
||||
}
|
||||
|
||||
function foo($q, $a) {
|
||||
$x = getX($q, $a);
|
||||
fiz($x->foo());
|
||||
}
|
||||
|
||||
foo(true, 1);
|
||||
foo(false, "a");
|
||||
|
||||
function fiz($a) {
|
||||
var_dump($a);
|
||||
}
|
||||
function getX($q, $a) {
|
||||
if ($q) $x = new X($a);
|
||||
return $x;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
int(1)
|
||||
HipHop Notice: Undefined variable: x in %s/slow/object_method/bad-inlining.php on line 26
|
||||
HipHop Fatal error: Uncaught exception 'BadMethodCallException' with message 'Call to a member function foo() on a non-object' in %s/slow/object_method/bad-inlining.php:15
|
||||
Stack trace:
|
||||
#0 %s/slow/object_method/bad-inlining.php(19): foo()
|
||||
#1 {main}
|
||||
@@ -491,7 +491,7 @@ std::string Process::GetCPUModel() {
|
||||
do_cpuid(0, regs);
|
||||
|
||||
const int vendor_size = sizeof(regs[1])*3;
|
||||
|
||||
std::swap(regs[2], regs[3]);
|
||||
uint32_t cpu_exthigh = 0;
|
||||
if (memcmp(regs + 1, "GenuineIntel", vendor_size) == 0 ||
|
||||
memcmp(regs + 1, "AuthenticAMD", vendor_size) == 0) {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
HHVM_VERSION(2.3.0-dev)
|
||||
HHVM_VERSION(2.3.0)
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário