Move NormalizedInstruction out of translator.h
In the name of making incremental builds faster when headers change, I've pulled NormalizedInstruction out of translator.h and replaced it with a forward decl.
Esse commit está contido em:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "hphp/runtime/vm/jit/annotation.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
#include "hphp/util/base.h"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "hphp/runtime/vm/runtime.h"
|
||||
#include "hphp/runtime/vm/jit/code-gen.h"
|
||||
#include "hphp/runtime/vm/jit/ir-factory.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
#include "hphp/runtime/vm/jit/translator-runtime.h"
|
||||
#include "hphp/runtime/vm/jit/translator-x64.h"
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
#include "hphp/runtime/vm/jit/ir.h"
|
||||
#include "hphp/runtime/vm/jit/ir-translator.h"
|
||||
#include "hphp/runtime/vm/jit/linear-scan.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/opt.h"
|
||||
#include "hphp/runtime/vm/jit/print.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
|
||||
// Include last to localize effects to this file
|
||||
#include "hphp/util/assert_throw.h"
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 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/runtime/vm/jit/normalized-instruction.h"
|
||||
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace Transl {
|
||||
|
||||
NormalizedInstruction::NormalizedInstruction()
|
||||
: next(nullptr)
|
||||
, prev(nullptr)
|
||||
, funcd(nullptr)
|
||||
, outStack(nullptr)
|
||||
, outLocal(nullptr)
|
||||
, outLocal2(nullptr)
|
||||
, outStack2(nullptr)
|
||||
, outStack3(nullptr)
|
||||
, outPred(Type::Gen)
|
||||
, checkedInputs(0)
|
||||
, outputPredicted(false)
|
||||
, outputPredictionStatic(false)
|
||||
, ignoreInnerType(false)
|
||||
, guardedThis(false)
|
||||
, guardedCls(false)
|
||||
, noSurprise(false)
|
||||
, noOp(false)
|
||||
, interp(false)
|
||||
, inlineReturn(false) {
|
||||
memset(imm, 0, sizeof(imm));
|
||||
}
|
||||
|
||||
NormalizedInstruction::~NormalizedInstruction() { }
|
||||
|
||||
/*
|
||||
* Helpers for recovering context of this instruction.
|
||||
*/
|
||||
Op NormalizedInstruction::op() const {
|
||||
auto op = toOp(*pc());
|
||||
assert(isValidOpcode(op));
|
||||
return (Op)op;
|
||||
}
|
||||
|
||||
Op NormalizedInstruction::mInstrOp() const {
|
||||
Op opcode = op();
|
||||
#define MII(instr, a, b, i, v, d) case Op##instr##M: return opcode;
|
||||
switch (opcode) {
|
||||
MINSTRS
|
||||
case OpFPassM:
|
||||
return preppedByRef ? OpVGetM : OpCGetM;
|
||||
default:
|
||||
not_reached();
|
||||
}
|
||||
#undef MII
|
||||
}
|
||||
|
||||
PC NormalizedInstruction::pc() const {
|
||||
return unit()->at(source.offset());
|
||||
}
|
||||
|
||||
const Unit* NormalizedInstruction::unit() const {
|
||||
return m_unit;
|
||||
}
|
||||
|
||||
const Func* NormalizedInstruction::func() const {
|
||||
return source.func();
|
||||
}
|
||||
|
||||
Offset NormalizedInstruction::offset() const {
|
||||
return source.offset();
|
||||
}
|
||||
|
||||
std::string NormalizedInstruction::toString() const {
|
||||
return instrToString((Op*)pc(), unit());
|
||||
}
|
||||
|
||||
SrcKey NormalizedInstruction::nextSk() const {
|
||||
return source.advanced(m_unit);
|
||||
}
|
||||
|
||||
NormalizedInstruction::OutputUse
|
||||
NormalizedInstruction::getOutputUsage(const DynLocation* output) const {
|
||||
for (NormalizedInstruction* succ = next; succ; succ = succ->next) {
|
||||
if (succ->noOp) continue;
|
||||
for (size_t i = 0; i < succ->inputs.size(); ++i) {
|
||||
if (succ->inputs[i] == output) {
|
||||
if (succ->inputWasInferred(i)) {
|
||||
return OutputUse::Inferred;
|
||||
}
|
||||
if (dontGuardAnyInputs(succ->op())) {
|
||||
/* the consumer doesnt care about its inputs
|
||||
but we may still have inferred something about
|
||||
its outputs that a later instruction may depend on
|
||||
*/
|
||||
if (!outputDependsOnInput(succ->op()) ||
|
||||
!(succ->outStack && !succ->outStack->rtt.isVagueValue() &&
|
||||
succ->getOutputUsage(succ->outStack) != OutputUse::Used) ||
|
||||
!(succ->outLocal && !succ->outLocal->rtt.isVagueValue() &&
|
||||
succ->getOutputUsage(succ->outLocal) != OutputUse::Used)) {
|
||||
return OutputUse::DoesntCare;
|
||||
}
|
||||
}
|
||||
return OutputUse::Used;
|
||||
}
|
||||
}
|
||||
}
|
||||
return OutputUse::Unused;
|
||||
}
|
||||
|
||||
bool NormalizedInstruction::isOutputUsed(const DynLocation* output) const {
|
||||
return (output && !output->rtt.isVagueValue() &&
|
||||
getOutputUsage(output) == OutputUse::Used);
|
||||
}
|
||||
|
||||
bool NormalizedInstruction::isAnyOutputUsed() const
|
||||
{
|
||||
return (isOutputUsed(outStack) ||
|
||||
isOutputUsed(outLocal));
|
||||
}
|
||||
|
||||
} } // HPHP::Transl
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 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_NORMALIZED_INSTRUCTION_H_
|
||||
#define incl_HPHP_NORMALIZED_INSTRUCTION_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
#include "hphp/runtime/base/smart_containers.h"
|
||||
#include "hphp/runtime/vm/bytecode.h"
|
||||
#include "hphp/runtime/vm/srckey.h"
|
||||
#include "hphp/runtime/vm/jit/type.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace Transl {
|
||||
|
||||
using JIT::Type;
|
||||
|
||||
struct DynLocation;
|
||||
struct Tracelet;
|
||||
|
||||
// A NormalizedInstruction has been decorated with its typed inputs and
|
||||
// outputs.
|
||||
class NormalizedInstruction {
|
||||
public:
|
||||
NormalizedInstruction* next;
|
||||
NormalizedInstruction* prev;
|
||||
|
||||
SrcKey source;
|
||||
const Func* funcd; // The Func in the topmost AR on the stack. Guaranteed to
|
||||
// be accurate. Don't guess about this. Note that this is
|
||||
// *not* the function whose body the NI belongs to.
|
||||
// Note that for an FPush* may be set to the (statically
|
||||
// known Func* that /this/ instruction is pushing)
|
||||
const StringData* funcName;
|
||||
// For FCall's, an opaque identifier that is either null, or uniquely
|
||||
// identifies the (functionName, -arity) pair of this call site.
|
||||
const Unit* m_unit;
|
||||
|
||||
std::vector<DynLocation*> inputs;
|
||||
DynLocation* outStack;
|
||||
DynLocation* outLocal;
|
||||
DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK,
|
||||
// MIterNextK
|
||||
DynLocation* outStack2; // Used for CGetL2
|
||||
DynLocation* outStack3; // Used for CGetL3
|
||||
Type outPred;
|
||||
vector<Location> deadLocs; // locations that die at the end of this
|
||||
// instruction
|
||||
ArgUnion imm[4];
|
||||
ImmVector immVec; // vector immediate; will have !isValid() if the
|
||||
// instruction has no vector immediate
|
||||
|
||||
// The member codes for the M-vector.
|
||||
std::vector<MemberCode> immVecM;
|
||||
|
||||
/*
|
||||
* For property dims, if we know the Class* for the base when we'll
|
||||
* be executing a given dim, it is stored here (at the index for the
|
||||
* relevant member code minus 1, because the known class for the
|
||||
* first member code is given by the base in inputs[]).
|
||||
*
|
||||
* Other entries here store null. See MetaInfo::MVecPropClass.
|
||||
*/
|
||||
std::vector<Class*> immVecClasses;
|
||||
|
||||
/*
|
||||
* On certain FCalls, we can inspect the callee and generate a
|
||||
* tracelet with information about what happens over there.
|
||||
*
|
||||
* The HHIR translator uses this to possibly inline callees.
|
||||
*/
|
||||
std::unique_ptr<Tracelet> calleeTrace;
|
||||
|
||||
unsigned checkedInputs;
|
||||
// StackOff: logical delta at *start* of this instruction to
|
||||
// stack at tracelet entry.
|
||||
int stackOffset;
|
||||
int sequenceNum;
|
||||
Offset nextOffset; // for intra-trace* non-call control-flow instructions,
|
||||
// this is the offset of the next instruction in the trace*
|
||||
bool breaksTracelet:1;
|
||||
bool changesPC:1;
|
||||
bool fuseBranch:1;
|
||||
bool preppedByRef:1;
|
||||
bool outputPredicted:1;
|
||||
bool outputPredictionStatic:1;
|
||||
bool ignoreInnerType:1;
|
||||
|
||||
/*
|
||||
* guardedThis indicates that we know that ar->m_this is
|
||||
* a valid $this. eg:
|
||||
*
|
||||
* $this->foo = 1; # needs to check that $this is non-null
|
||||
* $this->bar = 2; # can skip the check
|
||||
* return 5; # can decRef ar->m_this unconditionally
|
||||
*/
|
||||
bool guardedThis:1;
|
||||
|
||||
/*
|
||||
* guardedCls indicates that we know the class exists
|
||||
*/
|
||||
bool guardedCls:1;
|
||||
|
||||
/*
|
||||
* dont check the surprise flag
|
||||
*/
|
||||
bool noSurprise:1;
|
||||
|
||||
/*
|
||||
* instruction is statically known to have no effect, e.g. unboxing a Cell
|
||||
*/
|
||||
bool noOp:1;
|
||||
|
||||
/*
|
||||
* Used with HHIR. Instruction shoud be interpreted, because previous attempt
|
||||
* to translate it has failed.
|
||||
*/
|
||||
bool interp:1;
|
||||
|
||||
/*
|
||||
* Indicates that a RetC/RetV should generate inlined return code
|
||||
* rather than calling the shared stub.
|
||||
*/
|
||||
bool inlineReturn:1;
|
||||
|
||||
// For returns, this tracks local ids that are statically known not
|
||||
// to be reference counted at this point (i.e. won't require guards
|
||||
// or decrefs).
|
||||
boost::dynamic_bitset<> nonRefCountedLocals;
|
||||
|
||||
Op op() const;
|
||||
Op mInstrOp() const;
|
||||
PC pc() const;
|
||||
const Unit* unit() const;
|
||||
const Func* func() const;
|
||||
Offset offset() const;
|
||||
SrcKey nextSk() const;
|
||||
|
||||
NormalizedInstruction();
|
||||
~NormalizedInstruction();
|
||||
|
||||
void markInputInferred(int i) {
|
||||
if (i < 32) checkedInputs |= 1u << i;
|
||||
}
|
||||
|
||||
bool inputWasInferred(int i) const {
|
||||
return i < 32 && ((checkedInputs >> i) & 1);
|
||||
}
|
||||
|
||||
enum class OutputUse {
|
||||
Used,
|
||||
Unused,
|
||||
Inferred,
|
||||
DoesntCare
|
||||
};
|
||||
OutputUse getOutputUsage(const DynLocation* output) const;
|
||||
bool isOutputUsed(const DynLocation* output) const;
|
||||
bool isAnyOutputUsed() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Returns a DynLocation that will be destroyed with this
|
||||
// NormalizedInstruction.
|
||||
template<typename... Args>
|
||||
DynLocation* newDynLoc(Args&&... args) {
|
||||
m_dynLocs.push_back(
|
||||
smart::make_unique<DynLocation>(std::forward<Args>(args)...));
|
||||
return m_dynLocs.back().get();
|
||||
}
|
||||
|
||||
private:
|
||||
smart::vector<smart::unique_ptr<DynLocation>::type> m_dynLocs;
|
||||
};
|
||||
|
||||
} } // HPHP::Transl
|
||||
#endif
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <vector>
|
||||
#include "hphp/util/base.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "hphp/util/assertions.h"
|
||||
#include "hphp/util/map_walker.h"
|
||||
#include "hphp/runtime/base/runtime_option.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
#include "hphp/runtime/vm/jit/trans-cfg.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include "hphp/runtime/vm/jit/annotation.h"
|
||||
#include "hphp/runtime/vm/jit/hhbc-translator.h"
|
||||
#include "hphp/runtime/vm/jit/ir-translator.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
|
||||
namespace HPHP { namespace JIT {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 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/runtime/vm/jit/tracelet.h"
|
||||
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace Transl {
|
||||
|
||||
Tracelet::Tracelet() :
|
||||
m_stackChange(0),
|
||||
m_arState(),
|
||||
m_analysisFailed(false),
|
||||
m_inliningFailed(false){ }
|
||||
|
||||
Tracelet::~Tracelet() { }
|
||||
|
||||
NormalizedInstruction* Tracelet::newNormalizedInstruction() {
|
||||
NormalizedInstruction* ni = new NormalizedInstruction();
|
||||
m_instrs.push_back(ni);
|
||||
return ni;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation(Location l, DataType t) {
|
||||
DynLocation* dl = new DynLocation(l, t);
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation(Location l, RuntimeType t) {
|
||||
DynLocation* dl = new DynLocation(l, t);
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation() {
|
||||
DynLocation* dl = new DynLocation();
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
void Tracelet::print() const {
|
||||
print(std::cerr);
|
||||
}
|
||||
|
||||
void Tracelet::print(std::ostream& out) const {
|
||||
const NormalizedInstruction* i = m_instrStream.first;
|
||||
if (i == nullptr) {
|
||||
out << "<empty>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
out << i->unit()->filepath()->data() << ':'
|
||||
<< i->unit()->getLineNumber(i->offset()) << std::endl;
|
||||
for (; i; i = i->next) {
|
||||
out << " " << i->offset() << ": " << i->toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Tracelet::toString() const {
|
||||
std::ostringstream out;
|
||||
print(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
SrcKey Tracelet::nextSk() const {
|
||||
return m_instrStream.last->nextSk();
|
||||
}
|
||||
|
||||
const Func* Tracelet::func() const {
|
||||
return m_sk.func();
|
||||
}
|
||||
|
||||
} } // HPHP::Transl
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 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_TRACELET_H_
|
||||
#define incl_HPHP_TRACELET_H_
|
||||
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
|
||||
#include "hphp/util/base.h"
|
||||
#include "hphp/runtime/vm/jit/runtime-type.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace Transl {
|
||||
|
||||
struct NormalizedInstruction;
|
||||
|
||||
/*
|
||||
* A tracelet is a unit of input to the back-end. It is a partially typed,
|
||||
* non-maximal basic block, representing the next slice of the program to
|
||||
* be executed.
|
||||
* It is a consecutive set of instructions, only the last of which may be a
|
||||
* transfer of control, annotated types and locations for each opcode's input
|
||||
* and output.
|
||||
*/
|
||||
typedef hphp_hash_map<Location, DynLocation*, Location> ChangeMap;
|
||||
typedef ChangeMap DepMap;
|
||||
|
||||
struct InstrStream {
|
||||
InstrStream() : first(nullptr), last(nullptr) {}
|
||||
void append(NormalizedInstruction* ni);
|
||||
void remove(NormalizedInstruction* ni);
|
||||
NormalizedInstruction* first;
|
||||
NormalizedInstruction* last;
|
||||
};
|
||||
|
||||
struct RefDeps {
|
||||
struct Record {
|
||||
vector<bool> m_mask;
|
||||
vector<bool> m_vals;
|
||||
|
||||
std::string pretty() const {
|
||||
std::ostringstream out;
|
||||
out << "mask=";
|
||||
for (size_t i = 0; i < m_mask.size(); ++i) {
|
||||
out << (m_mask[i] ? "1" : "0");
|
||||
}
|
||||
out << " vals=";
|
||||
for (size_t i = 0; i < m_vals.size(); ++i) {
|
||||
out << (m_vals[i] ? "1" : "0");
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
};
|
||||
typedef hphp_hash_map<int64_t, Record, int64_hash> ArMap;
|
||||
ArMap m_arMap;
|
||||
|
||||
RefDeps() {}
|
||||
|
||||
void addDep(int entryArDelta, unsigned argNum, bool isRef) {
|
||||
if (m_arMap.find(entryArDelta) == m_arMap.end()) {
|
||||
m_arMap[entryArDelta] = Record();
|
||||
}
|
||||
Record& r = m_arMap[entryArDelta];
|
||||
if (argNum >= r.m_mask.size()) {
|
||||
assert(argNum >= r.m_vals.size());
|
||||
r.m_mask.resize(argNum + 1);
|
||||
r.m_vals.resize(argNum + 1);
|
||||
}
|
||||
r.m_mask[argNum] = true;
|
||||
r.m_vals[argNum] = isRef;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_arMap.size();
|
||||
}
|
||||
};
|
||||
|
||||
struct ActRecState {
|
||||
// State for tracking function param reffiness. m_topFunc is the function
|
||||
// for the activation record that is closest to the top of the stack, or
|
||||
// NULL if it is currently unknown. A tracelet can be in one of three
|
||||
// epistemological states: GUESSABLE, KNOWN, and UNKNOWABLE. We start out in
|
||||
// GUESSABLE, with m_topFunc == NULL (not yet guessed); when it's time to
|
||||
// guess, we will use the ActRec seen on the top of stack at compilation
|
||||
// time as a hint for refs going forward.
|
||||
//
|
||||
// The KNOWN state is a very strong guarantee. It means that no matter when
|
||||
// this tracelet is executed, no matter what else has happened, the ActRec
|
||||
// closest to the top of the stack WILL contain m_topFunc. This means: if that
|
||||
// function is defined conditionally, or defined in some other module, you
|
||||
// cannot correctly make that assertion. KNOWN indicates absolute certainty
|
||||
// about all possible futures.
|
||||
//
|
||||
// This strange "not-guessed-yet-but-could" state is required by our
|
||||
// VM design; at present, the ActRec is not easily recoverable from an
|
||||
// arbitrary instruction boundary. However, it can be recovered from the
|
||||
// instructions that need to do so.
|
||||
static const int InvalidEntryArDelta = INT_MAX;
|
||||
|
||||
enum class State {
|
||||
GUESSABLE, KNOWN, UNKNOWABLE
|
||||
};
|
||||
|
||||
struct Record {
|
||||
State m_state;
|
||||
const Func* m_topFunc;
|
||||
int m_entryArDelta; // delta at BB entry to guessed ActRec.
|
||||
};
|
||||
|
||||
std::vector<Record> m_arStack;
|
||||
|
||||
ActRecState() {}
|
||||
void pushFunc(const NormalizedInstruction& ni);
|
||||
void pushFuncD(const Func* func);
|
||||
void pushDynFunc();
|
||||
void pop();
|
||||
bool checkByRef(int argNum, int stackOffset, RefDeps* outRefDeps);
|
||||
const Func* knownFunc();
|
||||
State currentState();
|
||||
};
|
||||
|
||||
struct Tracelet : private boost::noncopyable {
|
||||
ChangeMap m_changes;
|
||||
DepMap m_dependencies;
|
||||
DepMap m_resolvedDeps; // dependencies resolved by static analysis
|
||||
InstrStream m_instrStream;
|
||||
int m_stackChange;
|
||||
|
||||
// SrcKey for the start of the Tracelet. This will be the same as
|
||||
// m_instrStream.first->source.
|
||||
SrcKey m_sk;
|
||||
|
||||
// numOpcodes is the number of raw opcode instructions, before optimization.
|
||||
// The immediates optimization may both:
|
||||
//
|
||||
// 1. remove the first opcode, thus making
|
||||
// sk.instr != instrs.first->source.instr
|
||||
// 2. remove no longer needed instructions
|
||||
int m_numOpcodes;
|
||||
|
||||
// Assumptions about entering actRec's reffiness.
|
||||
ActRecState m_arState;
|
||||
RefDeps m_refDeps;
|
||||
|
||||
/*
|
||||
* If we were unable to make sense of the instruction stream (e.g., it
|
||||
* used instructions that the translator does not understand), then this
|
||||
* tracelet is useful only for defining the boundaries of a basic block.
|
||||
* The low-level translator can handle this by backing off to the
|
||||
* bytecode interpreter.
|
||||
*/
|
||||
bool m_analysisFailed;
|
||||
|
||||
/*
|
||||
* If IR inlining failed we may still need access to the trace for profiling
|
||||
* purposes if stats are enabled so maintain this to verify that we should use
|
||||
* this Tracelet for inlining purposes.
|
||||
*/
|
||||
bool m_inliningFailed;
|
||||
|
||||
// Track which NormalizedInstructions and DynLocations are owned by this
|
||||
// Tracelet; used for cleanup purposes
|
||||
boost::ptr_vector<NormalizedInstruction> m_instrs;
|
||||
boost::ptr_vector<DynLocation> m_dynlocs;
|
||||
|
||||
Tracelet();
|
||||
~Tracelet();
|
||||
|
||||
NormalizedInstruction* newNormalizedInstruction();
|
||||
DynLocation* newDynLocation(Location l, DataType t);
|
||||
DynLocation* newDynLocation(Location l, RuntimeType t);
|
||||
DynLocation* newDynLocation();
|
||||
|
||||
/* These aren't merged into a single method with a default argument
|
||||
* to make gdb happy. */
|
||||
void print() const;
|
||||
void print(std::ostream& out) const;
|
||||
std::string toString() const;
|
||||
|
||||
SrcKey nextSk() const;
|
||||
const Func* func() const;
|
||||
};
|
||||
|
||||
} } // HPHP::Transl
|
||||
#endif
|
||||
@@ -92,11 +92,13 @@
|
||||
#include "hphp/runtime/vm/jit/code-gen.h"
|
||||
#include "hphp/runtime/vm/jit/hhbc-translator.h"
|
||||
#include "hphp/runtime/vm/jit/ir-translator.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/opt.h"
|
||||
#include "hphp/runtime/vm/jit/print.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
#include "hphp/runtime/vm/jit/srcdb.h"
|
||||
#include "hphp/runtime/vm/jit/target-cache.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
#include "hphp/runtime/vm/jit/unwind-x64.h"
|
||||
#include "hphp/runtime/vm/jit/x64-util.h"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "hphp/util/ringbuffer.h"
|
||||
#include "hphp/runtime/vm/debug/debug.h"
|
||||
#include "hphp/runtime/vm/jit/abi-x64.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/base/smart_containers.h"
|
||||
|
||||
namespace HPHP { class ExecutionContext; }
|
||||
|
||||
@@ -43,8 +43,10 @@
|
||||
#include "hphp/runtime/vm/jit/hhbc-translator.h"
|
||||
#include "hphp/runtime/vm/jit/ir-factory.h"
|
||||
#include "hphp/runtime/vm/jit/ir-translator.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
#include "hphp/runtime/vm/jit/target-cache.h"
|
||||
#include "hphp/runtime/vm/jit/tracelet.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
#include "hphp/runtime/vm/jit/translator-x64.h"
|
||||
#include "hphp/runtime/vm/jit/type.h"
|
||||
@@ -133,62 +135,6 @@ void InstrStream::remove(NormalizedInstruction* ni) {
|
||||
ni->next = nullptr;
|
||||
}
|
||||
|
||||
NormalizedInstruction* Tracelet::newNormalizedInstruction() {
|
||||
NormalizedInstruction* ni = new NormalizedInstruction();
|
||||
m_instrs.push_back(ni);
|
||||
return ni;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation(Location l, DataType t) {
|
||||
DynLocation* dl = new DynLocation(l, t);
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation(Location l, RuntimeType t) {
|
||||
DynLocation* dl = new DynLocation(l, t);
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
DynLocation* Tracelet::newDynLocation() {
|
||||
DynLocation* dl = new DynLocation();
|
||||
m_dynlocs.push_back(dl);
|
||||
return dl;
|
||||
}
|
||||
|
||||
void Tracelet::print() const {
|
||||
print(std::cerr);
|
||||
}
|
||||
|
||||
void Tracelet::print(std::ostream& out) const {
|
||||
const NormalizedInstruction* i = m_instrStream.first;
|
||||
if (i == nullptr) {
|
||||
out << "<empty>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
out << i->unit()->filepath()->data() << ':'
|
||||
<< i->unit()->getLineNumber(i->offset()) << std::endl;
|
||||
for (; i; i = i->next) {
|
||||
out << " " << i->offset() << ": " << i->toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Tracelet::toString() const {
|
||||
std::ostringstream out;
|
||||
print(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
SrcKey Tracelet::nextSk() const {
|
||||
return m_instrStream.last->nextSk();
|
||||
}
|
||||
|
||||
const Func* Tracelet::func() const {
|
||||
return m_sk.func();
|
||||
}
|
||||
|
||||
/*
|
||||
* locPhysicalOffset --
|
||||
*
|
||||
@@ -2454,52 +2400,6 @@ void TraceletContext::recordJmp() {
|
||||
m_numJmps++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers for recovering context of this instruction.
|
||||
*/
|
||||
Op NormalizedInstruction::op() const {
|
||||
auto op = toOp(*pc());
|
||||
assert(isValidOpcode(op));
|
||||
return (Op)op;
|
||||
}
|
||||
|
||||
Op NormalizedInstruction::mInstrOp() const {
|
||||
Op opcode = op();
|
||||
#define MII(instr, a, b, i, v, d) case Op##instr##M: return opcode;
|
||||
switch (opcode) {
|
||||
MINSTRS
|
||||
case OpFPassM:
|
||||
return preppedByRef ? OpVGetM : OpCGetM;
|
||||
default:
|
||||
not_reached();
|
||||
}
|
||||
#undef MII
|
||||
}
|
||||
|
||||
PC NormalizedInstruction::pc() const {
|
||||
return unit()->at(source.offset());
|
||||
}
|
||||
|
||||
const Unit* NormalizedInstruction::unit() const {
|
||||
return m_unit;
|
||||
}
|
||||
|
||||
const Func* NormalizedInstruction::func() const {
|
||||
return source.func();
|
||||
}
|
||||
|
||||
Offset NormalizedInstruction::offset() const {
|
||||
return source.offset();
|
||||
}
|
||||
|
||||
std::string NormalizedInstruction::toString() const {
|
||||
return instrToString((Op*)pc(), unit());
|
||||
}
|
||||
|
||||
SrcKey NormalizedInstruction::nextSk() const {
|
||||
return source.advanced(m_unit);
|
||||
}
|
||||
|
||||
void Translator::postAnalyze(NormalizedInstruction* ni, SrcKey& sk,
|
||||
Tracelet& t, TraceletContext& tas) {
|
||||
if (ni->op() == OpBareThis &&
|
||||
@@ -2522,46 +2422,6 @@ static bool isPop(const NormalizedInstruction* instr) {
|
||||
opc == OpPopR);
|
||||
}
|
||||
|
||||
NormalizedInstruction::OutputUse
|
||||
NormalizedInstruction::getOutputUsage(const DynLocation* output) const {
|
||||
for (NormalizedInstruction* succ = next; succ; succ = succ->next) {
|
||||
if (succ->noOp) continue;
|
||||
for (size_t i = 0; i < succ->inputs.size(); ++i) {
|
||||
if (succ->inputs[i] == output) {
|
||||
if (succ->inputWasInferred(i)) {
|
||||
return OutputUse::Inferred;
|
||||
}
|
||||
if (dontGuardAnyInputs(succ->op())) {
|
||||
/* the consumer doesnt care about its inputs
|
||||
but we may still have inferred something about
|
||||
its outputs that a later instruction may depend on
|
||||
*/
|
||||
if (!outputDependsOnInput(succ->op()) ||
|
||||
!(succ->outStack && !succ->outStack->rtt.isVagueValue() &&
|
||||
succ->getOutputUsage(succ->outStack) != OutputUse::Used) ||
|
||||
!(succ->outLocal && !succ->outLocal->rtt.isVagueValue() &&
|
||||
succ->getOutputUsage(succ->outLocal) != OutputUse::Used)) {
|
||||
return OutputUse::DoesntCare;
|
||||
}
|
||||
}
|
||||
return OutputUse::Used;
|
||||
}
|
||||
}
|
||||
}
|
||||
return OutputUse::Unused;
|
||||
}
|
||||
|
||||
bool NormalizedInstruction::isOutputUsed(const DynLocation* output) const {
|
||||
return (output && !output->rtt.isVagueValue() &&
|
||||
getOutputUsage(output) == OutputUse::Used);
|
||||
}
|
||||
|
||||
bool NormalizedInstruction::isAnyOutputUsed() const
|
||||
{
|
||||
return (isOutputUsed(outStack) ||
|
||||
isOutputUsed(outLocal));
|
||||
}
|
||||
|
||||
GuardType::GuardType(DataType outer, DataType inner)
|
||||
: outerType(outer), innerType(inner), klass(nullptr) {
|
||||
}
|
||||
@@ -4084,6 +3944,29 @@ const char *getTransKindName(TransKind kind) {
|
||||
return transKindStr[kind];
|
||||
}
|
||||
|
||||
TransRec::TransRec(SrcKey s,
|
||||
MD5 _md5,
|
||||
TransKind _kind,
|
||||
const Tracelet& t,
|
||||
TCA _aStart,
|
||||
uint32_t _aLen,
|
||||
TCA _astubsStart,
|
||||
uint32_t _astubsLen,
|
||||
TCA _counterStart,
|
||||
uint8_t _counterLen,
|
||||
vector<TransBCMapping> _bcMapping) :
|
||||
id(0), kind(_kind), src(s), md5(_md5),
|
||||
bcStopOffset(t.nextSk().offset()), aStart(_aStart), aLen(_aLen),
|
||||
astubsStart(_astubsStart), astubsLen(_astubsLen),
|
||||
counterStart(_counterStart), counterLen(_counterLen),
|
||||
bcMapping(_bcMapping) {
|
||||
for (DepMap::const_iterator dep = t.m_dependencies.begin();
|
||||
dep != t.m_dependencies.end();
|
||||
++dep) {
|
||||
dependencies.push_back(*dep->second);
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
TransRec::print(uint64_t profCount) const {
|
||||
const size_t kBufSize = 1000;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <set>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
|
||||
#include "hphp/util/hash.h"
|
||||
#include "hphp/util/timer.h"
|
||||
@@ -34,12 +33,10 @@
|
||||
#include "hphp/runtime/base/smart_containers.h"
|
||||
#include "hphp/runtime/vm/bytecode.h"
|
||||
#include "hphp/runtime/vm/jit/fixup.h"
|
||||
#include "hphp/runtime/vm/jit/region-selection.h"
|
||||
#include "hphp/runtime/vm/jit/runtime-type.h"
|
||||
#include "hphp/runtime/vm/jit/srcdb.h"
|
||||
#include "hphp/runtime/vm/jit/trans-data.h"
|
||||
#include "hphp/runtime/vm/jit/translator-instrs.h"
|
||||
#include "hphp/runtime/vm/jit/type.h"
|
||||
#include "hphp/runtime/vm/jit/write-lease.h"
|
||||
#include "hphp/runtime/vm/jit/prof-data.h"
|
||||
#include "hphp/runtime/vm/debugger_hook.h"
|
||||
@@ -179,181 +176,6 @@ enum TXFlags {
|
||||
struct Tracelet;
|
||||
struct TraceletContext;
|
||||
|
||||
// A NormalizedInstruction has been decorated with its typed inputs and
|
||||
// outputs.
|
||||
class NormalizedInstruction {
|
||||
public:
|
||||
NormalizedInstruction* next;
|
||||
NormalizedInstruction* prev;
|
||||
|
||||
SrcKey source;
|
||||
const Func* funcd; // The Func in the topmost AR on the stack. Guaranteed to
|
||||
// be accurate. Don't guess about this. Note that this is
|
||||
// *not* the function whose body the NI belongs to.
|
||||
// Note that for an FPush* may be set to the (statically
|
||||
// known Func* that /this/ instruction is pushing)
|
||||
const StringData* funcName;
|
||||
// For FCall's, an opaque identifier that is either null, or uniquely
|
||||
// identifies the (functionName, -arity) pair of this call site.
|
||||
const Unit* m_unit;
|
||||
|
||||
std::vector<DynLocation*> inputs;
|
||||
DynLocation* outStack;
|
||||
DynLocation* outLocal;
|
||||
DynLocation* outLocal2; // Used for IterInitK, MIterInitK, IterNextK,
|
||||
// MIterNextK
|
||||
DynLocation* outStack2; // Used for CGetL2
|
||||
DynLocation* outStack3; // Used for CGetL3
|
||||
Type outPred;
|
||||
vector<Location> deadLocs; // locations that die at the end of this
|
||||
// instruction
|
||||
ArgUnion imm[4];
|
||||
ImmVector immVec; // vector immediate; will have !isValid() if the
|
||||
// instruction has no vector immediate
|
||||
|
||||
// The member codes for the M-vector.
|
||||
std::vector<MemberCode> immVecM;
|
||||
|
||||
/*
|
||||
* For property dims, if we know the Class* for the base when we'll
|
||||
* be executing a given dim, it is stored here (at the index for the
|
||||
* relevant member code minus 1, because the known class for the
|
||||
* first member code is given by the base in inputs[]).
|
||||
*
|
||||
* Other entries here store null. See MetaInfo::MVecPropClass.
|
||||
*/
|
||||
std::vector<Class*> immVecClasses;
|
||||
|
||||
/*
|
||||
* On certain FCalls, we can inspect the callee and generate a
|
||||
* tracelet with information about what happens over there.
|
||||
*
|
||||
* The HHIR translator uses this to possibly inline callees.
|
||||
*/
|
||||
std::unique_ptr<Tracelet> calleeTrace;
|
||||
|
||||
unsigned checkedInputs;
|
||||
// StackOff: logical delta at *start* of this instruction to
|
||||
// stack at tracelet entry.
|
||||
int stackOffset;
|
||||
int sequenceNum;
|
||||
Offset nextOffset; // for intra-trace* non-call control-flow instructions,
|
||||
// this is the offset of the next instruction in the trace*
|
||||
bool breaksTracelet:1;
|
||||
bool changesPC:1;
|
||||
bool fuseBranch:1;
|
||||
bool preppedByRef:1;
|
||||
bool outputPredicted:1;
|
||||
bool outputPredictionStatic:1;
|
||||
bool ignoreInnerType:1;
|
||||
|
||||
/*
|
||||
* guardedThis indicates that we know that ar->m_this is
|
||||
* a valid $this. eg:
|
||||
*
|
||||
* $this->foo = 1; # needs to check that $this is non-null
|
||||
* $this->bar = 2; # can skip the check
|
||||
* return 5; # can decRef ar->m_this unconditionally
|
||||
*/
|
||||
bool guardedThis:1;
|
||||
|
||||
/*
|
||||
* guardedCls indicates that we know the class exists
|
||||
*/
|
||||
bool guardedCls:1;
|
||||
|
||||
/*
|
||||
* dont check the surprise flag
|
||||
*/
|
||||
bool noSurprise:1;
|
||||
|
||||
/*
|
||||
* instruction is statically known to have no effect, e.g. unboxing a Cell
|
||||
*/
|
||||
bool noOp:1;
|
||||
|
||||
/*
|
||||
* Used with HHIR. Instruction shoud be interpreted, because previous attempt
|
||||
* to translate it has failed.
|
||||
*/
|
||||
bool interp:1;
|
||||
|
||||
/*
|
||||
* Indicates that a RetC/RetV should generate inlined return code
|
||||
* rather than calling the shared stub.
|
||||
*/
|
||||
bool inlineReturn:1;
|
||||
|
||||
// For returns, this tracks local ids that are statically known not
|
||||
// to be reference counted at this point (i.e. won't require guards
|
||||
// or decrefs).
|
||||
boost::dynamic_bitset<> nonRefCountedLocals;
|
||||
|
||||
Op op() const;
|
||||
Op mInstrOp() const;
|
||||
PC pc() const;
|
||||
const Unit* unit() const;
|
||||
const Func* func() const;
|
||||
Offset offset() const;
|
||||
SrcKey nextSk() const;
|
||||
|
||||
NormalizedInstruction()
|
||||
: next(nullptr)
|
||||
, prev(nullptr)
|
||||
, funcd(nullptr)
|
||||
, outStack(nullptr)
|
||||
, outLocal(nullptr)
|
||||
, outLocal2(nullptr)
|
||||
, outStack2(nullptr)
|
||||
, outStack3(nullptr)
|
||||
, outPred(Type::Gen)
|
||||
, checkedInputs(0)
|
||||
, outputPredicted(false)
|
||||
, outputPredictionStatic(false)
|
||||
, ignoreInnerType(false)
|
||||
, guardedThis(false)
|
||||
, guardedCls(false)
|
||||
, noSurprise(false)
|
||||
, noOp(false)
|
||||
, interp(false)
|
||||
, inlineReturn(false)
|
||||
{
|
||||
memset(imm, 0, sizeof(imm));
|
||||
}
|
||||
|
||||
void markInputInferred(int i) {
|
||||
if (i < 32) checkedInputs |= 1u << i;
|
||||
}
|
||||
|
||||
bool inputWasInferred(int i) const {
|
||||
return i < 32 && ((checkedInputs >> i) & 1);
|
||||
}
|
||||
|
||||
enum class OutputUse {
|
||||
Used,
|
||||
Unused,
|
||||
Inferred,
|
||||
DoesntCare
|
||||
};
|
||||
OutputUse getOutputUsage(const DynLocation* output) const;
|
||||
bool isOutputUsed(const DynLocation* output) const;
|
||||
bool isAnyOutputUsed() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Returns a DynLocation that will be destroyed with this
|
||||
// NormalizedInstruction.
|
||||
template<typename... Args>
|
||||
DynLocation* newDynLoc(Args&&... args) {
|
||||
m_dynLocs.push_back(
|
||||
smart::make_unique<DynLocation>(std::forward<Args>(args)...));
|
||||
return m_dynLocs.back().get();
|
||||
}
|
||||
|
||||
private:
|
||||
smart::vector<smart::unique_ptr<DynLocation>::type> m_dynLocs;
|
||||
};
|
||||
|
||||
// Return a summary string of the bytecode in a tracelet.
|
||||
std::string traceletShape(const Tracelet&);
|
||||
|
||||
@@ -414,178 +236,10 @@ class GuardType {
|
||||
const Class* klass;
|
||||
};
|
||||
|
||||
/*
|
||||
* A tracelet is a unit of input to the back-end. It is a partially typed,
|
||||
* non-maximal basic block, representing the next slice of the program to
|
||||
* be executed.
|
||||
* It is a consecutive set of instructions, only the last of which may be a
|
||||
* transfer of control, annotated types and locations for each opcode's input
|
||||
* and output.
|
||||
*/
|
||||
typedef hphp_hash_map<Location, DynLocation*, Location> ChangeMap;
|
||||
typedef hphp_hash_map<Location,RuntimeType,Location> TypeMap;
|
||||
typedef ChangeMap DepMap;
|
||||
typedef hphp_hash_set<Location, Location> LocationSet;
|
||||
typedef hphp_hash_map<DynLocation*, GuardType> DynLocTypeMap;
|
||||
|
||||
struct InstrStream {
|
||||
InstrStream() : first(nullptr), last(nullptr) {}
|
||||
void append(NormalizedInstruction* ni);
|
||||
void remove(NormalizedInstruction* ni);
|
||||
NormalizedInstruction* first;
|
||||
NormalizedInstruction* last;
|
||||
};
|
||||
|
||||
struct RefDeps {
|
||||
struct Record {
|
||||
vector<bool> m_mask;
|
||||
vector<bool> m_vals;
|
||||
|
||||
std::string pretty() const {
|
||||
std::ostringstream out;
|
||||
out << "mask=";
|
||||
for (size_t i = 0; i < m_mask.size(); ++i) {
|
||||
out << (m_mask[i] ? "1" : "0");
|
||||
}
|
||||
out << " vals=";
|
||||
for (size_t i = 0; i < m_vals.size(); ++i) {
|
||||
out << (m_vals[i] ? "1" : "0");
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
};
|
||||
typedef hphp_hash_map<int64_t, Record, int64_hash> ArMap;
|
||||
ArMap m_arMap;
|
||||
|
||||
RefDeps() {}
|
||||
|
||||
void addDep(int entryArDelta, unsigned argNum, bool isRef) {
|
||||
if (m_arMap.find(entryArDelta) == m_arMap.end()) {
|
||||
m_arMap[entryArDelta] = Record();
|
||||
}
|
||||
Record& r = m_arMap[entryArDelta];
|
||||
if (argNum >= r.m_mask.size()) {
|
||||
assert(argNum >= r.m_vals.size());
|
||||
r.m_mask.resize(argNum + 1);
|
||||
r.m_vals.resize(argNum + 1);
|
||||
}
|
||||
r.m_mask[argNum] = true;
|
||||
r.m_vals[argNum] = isRef;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_arMap.size();
|
||||
}
|
||||
};
|
||||
|
||||
struct ActRecState {
|
||||
// State for tracking function param reffiness. m_topFunc is the function
|
||||
// for the activation record that is closest to the top of the stack, or
|
||||
// NULL if it is currently unknown. A tracelet can be in one of three
|
||||
// epistemological states: GUESSABLE, KNOWN, and UNKNOWABLE. We start out in
|
||||
// GUESSABLE, with m_topFunc == NULL (not yet guessed); when it's time to
|
||||
// guess, we will use the ActRec seen on the top of stack at compilation
|
||||
// time as a hint for refs going forward.
|
||||
//
|
||||
// The KNOWN state is a very strong guarantee. It means that no matter when
|
||||
// this tracelet is executed, no matter what else has happened, the ActRec
|
||||
// closest to the top of the stack WILL contain m_topFunc. This means: if that
|
||||
// function is defined conditionally, or defined in some other module, you
|
||||
// cannot correctly make that assertion. KNOWN indicates absolute certainty
|
||||
// about all possible futures.
|
||||
//
|
||||
// This strange "not-guessed-yet-but-could" state is required by our
|
||||
// VM design; at present, the ActRec is not easily recoverable from an
|
||||
// arbitrary instruction boundary. However, it can be recovered from the
|
||||
// instructions that need to do so.
|
||||
static const int InvalidEntryArDelta = INT_MAX;
|
||||
|
||||
enum class State {
|
||||
GUESSABLE, KNOWN, UNKNOWABLE
|
||||
};
|
||||
|
||||
struct Record {
|
||||
State m_state;
|
||||
const Func* m_topFunc;
|
||||
int m_entryArDelta; // delta at BB entry to guessed ActRec.
|
||||
};
|
||||
|
||||
std::vector<Record> m_arStack;
|
||||
|
||||
ActRecState() {}
|
||||
void pushFunc(const NormalizedInstruction& ni);
|
||||
void pushFuncD(const Func* func);
|
||||
void pushDynFunc();
|
||||
void pop();
|
||||
bool checkByRef(int argNum, int stackOffset, RefDeps* outRefDeps);
|
||||
const Func* knownFunc();
|
||||
State currentState();
|
||||
};
|
||||
|
||||
struct Tracelet : private boost::noncopyable {
|
||||
ChangeMap m_changes;
|
||||
DepMap m_dependencies;
|
||||
DepMap m_resolvedDeps; // dependencies resolved by static analysis
|
||||
InstrStream m_instrStream;
|
||||
int m_stackChange;
|
||||
|
||||
// SrcKey for the start of the Tracelet. This will be the same as
|
||||
// m_instrStream.first->source.
|
||||
SrcKey m_sk;
|
||||
|
||||
// numOpcodes is the number of raw opcode instructions, before optimization.
|
||||
// The immediates optimization may both:
|
||||
//
|
||||
// 1. remove the first opcode, thus making
|
||||
// sk.instr != instrs.first->source.instr
|
||||
// 2. remove no longer needed instructions
|
||||
int m_numOpcodes;
|
||||
|
||||
// Assumptions about entering actRec's reffiness.
|
||||
ActRecState m_arState;
|
||||
RefDeps m_refDeps;
|
||||
|
||||
/*
|
||||
* If we were unable to make sense of the instruction stream (e.g., it
|
||||
* used instructions that the translator does not understand), then this
|
||||
* tracelet is useful only for defining the boundaries of a basic block.
|
||||
* The low-level translator can handle this by backing off to the
|
||||
* bytecode interpreter.
|
||||
*/
|
||||
bool m_analysisFailed;
|
||||
|
||||
/*
|
||||
* If IR inlining failed we may still need access to the trace for profiling
|
||||
* purposes if stats are enabled so maintain this to verify that we should use
|
||||
* this Tracelet for inlining purposes.
|
||||
*/
|
||||
bool m_inliningFailed;
|
||||
|
||||
// Track which NormalizedInstructions and DynLocations are owned by this
|
||||
// Tracelet; used for cleanup purposes
|
||||
boost::ptr_vector<NormalizedInstruction> m_instrs;
|
||||
boost::ptr_vector<DynLocation> m_dynlocs;
|
||||
|
||||
Tracelet() :
|
||||
m_stackChange(0),
|
||||
m_arState(),
|
||||
m_analysisFailed(false),
|
||||
m_inliningFailed(false){ }
|
||||
|
||||
NormalizedInstruction* newNormalizedInstruction();
|
||||
DynLocation* newDynLocation(Location l, DataType t);
|
||||
DynLocation* newDynLocation(Location l, RuntimeType t);
|
||||
DynLocation* newDynLocation();
|
||||
|
||||
/* These aren't merged into a single method with a default argument
|
||||
* to make gdb happy. */
|
||||
void print() const;
|
||||
void print(std::ostream& out) const;
|
||||
std::string toString() const;
|
||||
|
||||
SrcKey nextSk() const;
|
||||
const Func* func() const;
|
||||
};
|
||||
|
||||
const char* getTransKindName(TransKind kind);
|
||||
|
||||
@@ -635,23 +289,12 @@ struct TransRec {
|
||||
TransKind _kind,
|
||||
const Tracelet& t,
|
||||
TCA _aStart = 0,
|
||||
uint32_t _aLen = 0,
|
||||
uint32_t _aLen = 0,
|
||||
TCA _astubsStart = 0,
|
||||
uint32_t _astubsLen = 0,
|
||||
uint32_t _astubsLen = 0,
|
||||
TCA _counterStart = 0,
|
||||
uint8_t _counterLen = 0,
|
||||
vector<TransBCMapping> _bcMapping = vector<TransBCMapping>()) :
|
||||
id(0), kind(_kind), src(s), md5(_md5),
|
||||
bcStopOffset(t.nextSk().offset()), aStart(_aStart), aLen(_aLen),
|
||||
astubsStart(_astubsStart), astubsLen(_astubsLen),
|
||||
counterStart(_counterStart), counterLen(_counterLen),
|
||||
bcMapping(_bcMapping) {
|
||||
for (DepMap::const_iterator dep = t.m_dependencies.begin();
|
||||
dep != t.m_dependencies.end();
|
||||
++dep) {
|
||||
dependencies.push_back(*dep->second);
|
||||
}
|
||||
}
|
||||
uint8_t _counterLen = 0,
|
||||
vector<TransBCMapping> _bcMapping = vector<TransBCMapping>());
|
||||
|
||||
void setID(TransID newID) { id = newID; }
|
||||
string print(uint64_t profCount) const;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "hphp/runtime/vm/jit/hhbc-translator.h"
|
||||
#include "hphp/runtime/vm/jit/ir.h"
|
||||
#include "hphp/runtime/vm/jit/ir-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/normalized-instruction.h"
|
||||
#include "hphp/runtime/vm/jit/translator-x64.h"
|
||||
|
||||
// These files do ugly things with macros so include them last
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário