hphp/runtime/vm/jit/extra-data.h (2,322 lines of code) (raw):
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present 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. |
+----------------------------------------------------------------------+
*/
#pragma once
#include "hphp/runtime/base/collections.h"
#include "hphp/runtime/base/typed-value.h"
#include "hphp/runtime/vm/bytecode.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/vm/iter.h"
#include "hphp/runtime/vm/srckey.h"
#include "hphp/runtime/vm/jit/alias-class.h"
#include "hphp/runtime/vm/jit/types.h"
#include "hphp/runtime/vm/jit/ir-opcode.h"
#include "hphp/runtime/vm/jit/stack-offsets.h"
#include "hphp/runtime/ext/generator/ext_generator.h"
#include "hphp/util/arena.h"
#include "hphp/util/optional.h"
#include "hphp/util/ringbuffer.h"
#include "hphp/util/safe-cast.h"
#include <folly/Conv.h>
#include <folly/Hash.h>
#include <folly/gen/Base.h>
#include <folly/gen/String.h>
#include <algorithm>
#include <string>
namespace HPHP::jit {
//////////////////////////////////////////////////////////////////////
/*
* Some IRInstructions with compile-time-only constants may carry along extra
* data in the form of one of these structures.
*
* Note that this isn't really appropriate for compile-time constants that are
* actually representing user values (we want them to be visible to
* optimization passes, allocatable to registers, etc), just compile-time
* metadata.
*
* These types must:
*
* - Derive from IRExtraData (for overloading purposes).
* - Be arena-allocatable (no non-trivial destructors).
* - Either CopyConstructible, or implement a clone member function that
* takes an arena to clone to.
* - Implement an equals() member that indicates equality.
* - Implement a stableHash() method that is invariant across process
* restarts.
*
* In addition, extra data belonging to IRInstructions that may be hashed in
* IRInstrTables must:
*
* - Implement a hash() method.
*
* Finally, optionally they may implement a show() method for use in debug
* printouts.
*/
/*
* Traits that returns the type of the extra C++ data structure for a
* given instruction, if it has one, along with some other information
* about the type.
*/
template<Opcode op> struct OpHasExtraData { enum { value = 0 }; };
template<Opcode op> struct IRExtraDataType;
template<typename T> struct SharedProfileEntry;
struct DecRefProfile;
using DecRefProfileEntry = SharedProfileEntry<DecRefProfile>;
//////////////////////////////////////////////////////////////////////
struct IRExtraData {};
//////////////////////////////////////////////////////////////////////
/*
* Shared IRExtraData classes.
*
* These subtypes represent common parameters (e.g., statically known Func,
* local variable ID, stack offset, etc.) that are useful for a variety of HHIR
* instructions.
*
* These are kept separate from the one-off IRExtraDatas to make it easier to
* find existing common parameters.
*/
/*
* Class pointer.
*
* Required to be non-null.
*/
struct ClassData : IRExtraData {
explicit ClassData(const Class* cls)
: cls(cls)
{
assertx(cls != nullptr);
}
std::string show() const {
return folly::to<std::string>(cls->name()->data());
}
bool equals(const ClassData& o) const {
return cls == o.cls;
}
size_t hash() const {
return pointer_hash<Class>()(cls);
}
size_t stableHash() const {
return cls->stableHash();
}
const Class* cls;
};
struct OptClassData : IRExtraData {
explicit OptClassData(const Class* cls)
: cls(cls)
{}
std::string show() const {
return folly::to<std::string>(cls ? cls->name()->data() : "{null}");
}
bool equals(const OptClassData& o) const {
return cls == o.cls;
}
size_t hash() const {
return pointer_hash<Class>()(cls);
}
size_t stableHash() const {
return (cls ? cls->stableHash() : 0);
}
const Class* cls;
};
/*
* Class pointer, suppress flag, is or as operation flag and range into the
* stack (for type structures) needed for resolve type struct instruction
*
* Class pointer could be null.
*/
struct ResolveTypeStructData : IRExtraData {
explicit ResolveTypeStructData(
const Class* cls,
bool suppress,
IRSPRelOffset offset,
uint32_t size,
bool isOrAsOp
)
: cls(cls)
, suppress(suppress)
, offset(offset)
, size(size)
, isOrAsOp(isOrAsOp)
{}
std::string show() const {
return folly::sformat("{},{},{},{},{}",
cls ? cls->name()->data() : "nullptr",
suppress ? "suppress" : "no-suppress",
offset.offset,
size,
isOrAsOp);
}
bool equals(const ResolveTypeStructData& o) const {
return cls == o.cls && suppress == o.suppress &&
offset == o.offset && size == o.size && isOrAsOp == o.isOrAsOp;
}
size_t hash() const {
return (pointer_hash<Class>()(cls)
+ std::hash<int32_t>()(offset.offset)
+ std::hash<uint32_t>()(size))
^ ((int64_t)(suppress ? -1 : 0) << 32 | (isOrAsOp ? -1 : 0));
}
size_t stableHash() const {
return folly::hash::hash_combine(
cls ? cls->stableHash() : 0,
std::hash<int32_t>()(offset.offset),
std::hash<uint32_t>()(size),
std::hash<uint8_t>()(suppress << 1 | isOrAsOp)
);
}
const Class* cls;
bool suppress;
IRSPRelOffset offset;
uint32_t size;
bool isOrAsOp;
};
/*
* ExtendsClass.
*/
struct ExtendsClassData : IRExtraData {
explicit ExtendsClassData(const Class* cls, bool strictLikely = false)
: cls(cls), strictLikely(strictLikely)
{
assertx(cls != nullptr);
}
std::string show() const {
return folly::sformat("{}{}",
cls->name(), strictLikely ? ":strictLikely" : "");
}
bool equals(const ExtendsClassData& o) const {
return cls == o.cls && strictLikely == o.strictLikely;
}
size_t hash() const {
return pointer_hash<Class>()(cls) ^ (strictLikely ? -1 : 0);
}
size_t stableHash() const {
return cls->stableHash() ^ (strictLikely ? -1 : 0);
}
const Class* cls;
bool strictLikely;
};
/*
* InstanceOfIfaceVtable.
*/
struct InstanceOfIfaceVtableData : IRExtraData {
InstanceOfIfaceVtableData(const Class* cls, bool canOptimize)
: cls(cls), canOptimize(canOptimize)
{
assertx(cls != nullptr);
}
std::string show() const {
return folly::sformat("{}{}",
cls->name(), canOptimize ? ":canOptimize" : "");
}
bool equals(const InstanceOfIfaceVtableData& o) const {
return cls == o.cls && canOptimize == o.canOptimize;
}
size_t hash() const {
return pointer_hash<Class>()(cls) ^ (canOptimize ? -1 : 0);
}
size_t stableHash() const {
return cls->stableHash() ^ (canOptimize ? -1 : 0);
}
const Class* cls;
bool canOptimize;
};
/*
* Class with method name.
*/
struct ClsMethodData : IRExtraData {
ClsMethodData(const StringData* cls, const StringData* method,
const NamedEntity* ne, const Class* context)
: clsName(cls)
, methodName(method)
, namedEntity(ne)
, context(context)
{}
std::string show() const {
return folly::sformat(
"{}::{} ({})",
clsName,
methodName,
context ? context->name()->data() : "{no context}");
}
bool equals(const ClsMethodData& o) const {
// The strings are static so we can use pointer equality.
return
clsName == o.clsName &&
methodName == o.methodName &&
context == o.context;
}
size_t hash() const {
return folly::hash::hash_combine(
std::hash<const StringData*>()(clsName),
std::hash<const StringData*>()(methodName),
std::hash<const Class*>()(context)
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
clsName->hashStatic(),
methodName->hashStatic(),
context ? context->stableHash() : 0
);
}
const StringData* clsName;
const StringData* methodName;
const NamedEntity* namedEntity;
const Class* context;
};
struct IfaceMethodData : IRExtraData {
IfaceMethodData(Slot vtableIdx, Slot methodIdx)
: vtableIdx(vtableIdx)
, methodIdx(methodIdx)
{}
std::string show() const {
return folly::sformat("{}, {}", vtableIdx, methodIdx);
}
bool equals(const IfaceMethodData& o) const {
return vtableIdx == o.vtableIdx && methodIdx == o.methodIdx;
}
size_t hash() const {
return hash_int64((int64_t)vtableIdx << 32 | methodIdx);
}
size_t stableHash() const {
return hash_int64((int64_t)vtableIdx << 32 | methodIdx);
}
Slot vtableIdx;
Slot methodIdx;
};
/*
* Func
*/
struct FuncData : IRExtraData {
explicit FuncData(const Func* f)
: func(f)
{}
std::string show() const {
return folly::format("{}", func->fullName()).str();
}
bool equals(const FuncData& f) const {
return func == f.func;
}
size_t stableHash() const {
return func->stableHash();
}
const Func* func;
};
/*
* Func with argument index.
*/
struct FuncArgData : IRExtraData {
explicit FuncArgData(const Func* f, int64_t arg)
: func(f)
, argNum(arg)
{}
std::string show() const {
return folly::format("{},{}", func->name(), argNum).str();
}
bool equals(const FuncArgData& o) const {
return func == o.func && argNum == o.argNum;
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<int64_t>()(argNum)
);
}
const Func* func;
int64_t argNum;
};
/*
* Func with argument index and expected type.
*/
struct FuncArgTypeData : IRExtraData {
explicit FuncArgTypeData(const Func* f, int64_t arg, const StringData* t)
: func(f)
, argNum(arg)
, type(t)
{}
std::string show() const {
return folly::format("{},{},{}", func->name(), argNum, type).str();
}
bool equals(const FuncArgTypeData& o) const {
return func == o.func && argNum == o.argNum && type == o.type;
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<int64_t>()(argNum),
type->hashStatic()
);
}
const Func* func;
int64_t argNum;
const StringData* type;
};
/*
* Local variable ID.
*/
struct LocalId : IRExtraData {
explicit LocalId(uint32_t id) : locId(id) {}
std::string show() const { return folly::to<std::string>(locId); }
bool equals(LocalId o) const { return locId == o.locId; }
size_t hash() const { return std::hash<uint32_t>()(locId); }
size_t stableHash() const { return std::hash<uint32_t>()(locId); }
uint32_t locId;
};
/*
* Index into, e.g., an array.
*/
struct IndexData : IRExtraData {
explicit IndexData(uint32_t index) : index(index) {}
std::string show() const { return folly::format("{}", index).str(); }
size_t hash() const { return std::hash<uint32_t>()(index); }
size_t stableHash() const { return std::hash<uint32_t>()(index); }
bool equals(const IndexData& o) const {
return index == o.index;
}
uint32_t index;
};
/*
* An index and a key used to initialized a dict-ish array element.
*/
struct KeyedIndexData : IRExtraData {
explicit KeyedIndexData(uint32_t index, const StringData* key)
: index(index)
, key(key)
{}
std::string show() const;
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(index),
key->hashStatic()
);
}
bool equals(const KeyedIndexData& o) const {
return index == o.index && key == o.key;
}
uint32_t index;
const StringData* key;
};
/*
* Used to optimize array accesses. Does not change semantics, but changes how
* we do the lookup - e.g. we scan small static arrays for static string keys.
*
* NOTE: Currently, we only use this hint in DictIdx. We may want
* to use it for ArrayExists / etc.
*/
struct SizeHintData : IRExtraData {
enum SizeHint {
Default,
SmallStatic
};
SizeHintData() : hint(SizeHint::Default) {}
explicit SizeHintData(SizeHint hint) : hint(hint) {}
std::string show() const {
switch (hint) {
case SizeHint::Default: return "Default";
case SizeHint::SmallStatic: return "SmallStatic";
}
not_reached();
}
size_t hash() const {
return stableHash();
}
size_t stableHash() const {
return std::hash<SizeHint>()(hint);
}
bool equals(const SizeHintData& o) const {
return hint == o.hint;
}
SizeHint hint;
};
/*
* Iterator ID.
*/
struct IterId : IRExtraData {
explicit IterId(uint32_t id)
: iterId(id)
{}
std::string show() const { return folly::to<std::string>(iterId); }
bool equals(IterId o) const { return iterId == o.iterId; }
size_t hash() const { return std::hash<uint32_t>()(iterId); }
size_t stableHash() const { return std::hash<uint32_t>()(iterId); }
uint32_t iterId;
};
/*
* Iter instruction data, used for both key-value and value-only iterators.
* Check args.hasKey() to distinguish between the two.
*/
struct IterData : IRExtraData {
explicit IterData(IterArgs args) : args(args) {}
std::string show() const {
return HPHP::show(args, [&](int32_t id) {
return folly::to<std::string>(id);
});
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(args.iterId),
std::hash<int32_t>()(args.keyId),
std::hash<int32_t>()(args.valId),
std::hash<IterArgs::Flags>()(args.flags)
);
}
bool equals(const IterData& o) const {
return args == o.args;
}
IterArgs args;
};
struct IterTypeData : IRExtraData {
IterTypeData(uint32_t iterId, IterSpecialization type, ArrayLayout layout)
: iterId{iterId}
, type{type}
, layout{layout}
{
always_assert(type.specialized);
}
std::string show() const {
auto const type_str = HPHP::show(type);
auto const layout_str = layout.describe();
return folly::format("{}::{}::{}", iterId, type_str, layout_str).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(iterId),
std::hash<uint8_t>()(type.as_byte),
std::hash<uint16_t>()(layout.toUint16())
);
}
bool equals(const IterTypeData& o) const {
return iterId == o.iterId && type.as_byte == o.type.as_byte &&
layout == o.layout;
}
uint32_t iterId;
IterSpecialization type;
ArrayLayout layout;
};
struct IterOffsetData : IRExtraData {
IterOffsetData(int16_t offset) : offset(offset) {}
std::string show() const { return folly::to<std::string>(offset); }
size_t stableHash() const { return std::hash<int16_t>()(offset); }
bool equals(const IterOffsetData& o) const { return offset == o.offset; }
int16_t offset;
};
/*
* RDS handle.
*/
struct RDSHandleData : IRExtraData {
explicit RDSHandleData(rds::Handle handle) : handle(handle) {}
std::string show() const { return folly::to<std::string>(handle); }
bool equals(RDSHandleData o) const { return handle == o.handle; }
size_t hash() const { return std::hash<uint32_t>()(handle); }
size_t stableHash() const {
auto const sym = rds::reverseLink(handle);
if (!sym) return 0;
return rds::symbol_stable_hash(*sym);
}
rds::Handle handle;
};
struct RDSHandleAndType : RDSHandleData {
RDSHandleAndType(rds::Handle handle, Type type)
: RDSHandleData{handle}, type{type} { assertx(type <= TCell); }
std::string show() const {
return folly::sformat("{},{}", handle, type);
}
bool equals(const RDSHandleAndType& o) const {
return handle == o.handle && type == o.type;
}
size_t hash() const {
return folly::hash::hash_combine(
RDSHandleData::hash(),
type.hash()
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
RDSHandleData::stableHash(),
type.stableHash()
);
}
Type type;
};
struct ArrayAccessProfileData : RDSHandleData {
ArrayAccessProfileData(rds::Handle handle, DecRefProfileEntry* extra)
: RDSHandleData(handle), extra{extra} {}
std::string show() const {
if (!extra) return RDSHandleData::show();
return folly::sformat("{},{}", handle, reinterpret_cast<void*>(extra));
}
bool equals(const ArrayAccessProfileData& o) const {
return handle == o.handle && extra == o.extra;
}
DecRefProfileEntry* extra;
};
struct TVInRDSHandleData : RDSHandleData {
TVInRDSHandleData (rds::Handle handle, bool includeAux)
: RDSHandleData(handle), includeAux(includeAux) {}
std::string show() const {
return folly::to<std::string>(handle, ",", includeAux);
}
bool equals(TVInRDSHandleData o) const {
return handle == o.handle && includeAux == o.includeAux;
}
size_t hash() const {
return folly::hash::hash_combine(std::hash<uint32_t>()(handle),
std::hash<bool>()(includeAux));
}
size_t stableHash() const {
return folly::hash::hash_combine(RDSHandleData::stableHash(),
std::hash<bool>()(includeAux));
}
bool includeAux;
};
/*
* Translation ID.
*
* Used with profiling-related instructions.
*/
struct TransIDData : IRExtraData {
explicit TransIDData(TransID transId) : transId(transId) {}
std::string show() const { return folly::to<std::string>(transId); }
size_t stableHash() const {
return std::hash<TransID>()(transId);
}
bool equals(const TransIDData& o) const {
return transId == o.transId;
}
TransID transId;
};
struct DefFPData : IRExtraData {
explicit DefFPData(Optional<IRSPRelOffset> offset) : offset(offset) {}
std::string show() const {
if (!offset) return "IRSPOff unknown";
return folly::to<std::string>("IRSPOff ", offset->offset);
}
bool equals(DefFPData o) const { return offset == o.offset; }
size_t stableHash() const {
return offset ? std::hash<int32_t>()(offset->offset) : 0;
}
// Frame position on the stack, if it lives there and the position is known.
Optional<IRSPRelOffset> offset;
};
/*
* Stack pointer offset.
*/
struct DefStackData : IRExtraData {
explicit DefStackData(SBInvOffset irSPOff, SBInvOffset bcSPOff)
: irSPOff(irSPOff)
, bcSPOff(bcSPOff)
{}
std::string show() const {
return folly::sformat("irSPOff={}, bcSPOff={}",
irSPOff.offset, bcSPOff.offset);
}
bool equals(DefStackData o) const {
return irSPOff == o.irSPOff && bcSPOff == o.bcSPOff;
}
size_t hash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(irSPOff.offset),
std::hash<int32_t>()(bcSPOff.offset)
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(irSPOff.offset),
std::hash<int32_t>()(bcSPOff.offset)
);
}
SBInvOffset irSPOff; // offset from stack base to vmsp()
SBInvOffset bcSPOff; // offset from stack base to top of the stack
};
/*
* Stack offset.
*/
struct IRSPRelOffsetData : IRExtraData {
explicit IRSPRelOffsetData(IRSPRelOffset offset) : offset(offset) {}
std::string show() const {
return folly::to<std::string>("IRSPOff ", offset.offset);
}
bool equals(IRSPRelOffsetData o) const { return offset == o.offset; }
size_t hash() const { return std::hash<int32_t>()(offset.offset); }
size_t stableHash() const { return std::hash<int32_t>()(offset.offset); }
IRSPRelOffset offset;
};
///////////////////////////////////////////////////////////////////////////////
/*
* One-off IRExtraData classes.
*
* These are used for only one or two instructions and are in no particular
* order. Add new IRExtraData types here.
*/
struct IsAsyncData : IRExtraData {
explicit IsAsyncData(bool isAsync) : isAsync(isAsync) {}
std::string show() const { return folly::to<std::string>(isAsync); }
bool equals(IsAsyncData d) const { return isAsync == d.isAsync; }
size_t hash() const { return std::hash<int32_t>()(isAsync); }
size_t stableHash() const { return std::hash<int32_t>()(isAsync); }
bool isAsync;
};
struct LdBindAddrData : IRExtraData {
explicit LdBindAddrData(SrcKey sk, SBInvOffset bcSPOff)
: sk(sk)
, bcSPOff(bcSPOff)
{}
std::string show() const { return showShort(sk); }
size_t stableHash() const {
return folly::hash::hash_combine(
SrcKey::StableHasher()(sk),
std::hash<int32_t>()(bcSPOff.offset)
);
}
bool equals(const LdBindAddrData& o) const {
return sk == o.sk && bcSPOff == o.bcSPOff;
}
SrcKey sk;
SBInvOffset bcSPOff;
};
struct LdSSwitchData : IRExtraData {
struct Elm {
const StringData* str;
SrcKey dest;
};
explicit LdSSwitchData() = default;
LdSSwitchData(const LdSSwitchData&) = delete;
LdSSwitchData& operator=(const LdSSwitchData&) = delete;
LdSSwitchData* clone(Arena& arena) const {
LdSSwitchData* target = new (arena) LdSSwitchData;
target->numCases = numCases;
target->defaultSk = defaultSk;
target->cases = new (arena) Elm[numCases];
target->bcSPOff = bcSPOff;
std::copy(cases, cases + numCases, const_cast<Elm*>(target->cases));
return target;
}
std::string show() const {
return folly::to<std::string>(bcSPOff.offset);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int64_t>()(numCases),
SrcKey::StableHasher()(defaultSk),
std::hash<int32_t>()(bcSPOff.offset)
);
}
bool equals(const LdSSwitchData& o) const {
if (numCases != o.numCases) return false;
if (defaultSk != o.defaultSk) return false;
if (bcSPOff != o.bcSPOff) return false;
for (int64_t i = 0; i < numCases; i++) {
if (cases[i].dest != o.cases[i].dest) return false;
if (cases[i].str != o.cases[i].str) return false;
}
return true;
}
int64_t numCases;
const Elm* cases;
SrcKey defaultSk;
SBInvOffset bcSPOff;
};
struct ProfileSwitchData : IRExtraData {
ProfileSwitchData(rds::Handle handle, int32_t cases, int64_t base)
: handle(handle)
, cases(cases)
, base(base)
{}
std::string show() const {
return folly::sformat("handle {}, {} cases, base {}", handle, cases, base);
}
size_t stableHash() const {
auto const sym = rds::reverseLink(handle);
return folly::hash::hash_combine(
sym ? rds::symbol_stable_hash(*sym) : 0,
std::hash<int32_t>()(cases),
std::hash<int64_t>()(base)
);
}
bool equals(const ProfileSwitchData& o) const {
return handle == o.handle && cases == o.cases && base == o.base;
}
rds::Handle handle;
int32_t cases;
int64_t base;
};
struct JmpSwitchData : IRExtraData {
JmpSwitchData* clone(Arena& arena) const {
JmpSwitchData* sd = new (arena) JmpSwitchData;
sd->cases = cases;
sd->targets = new (arena) SrcKey[cases];
sd->spOffBCFromStackBase = spOffBCFromStackBase;
sd->spOffBCFromIRSP = spOffBCFromIRSP;
std::copy(targets, targets + cases, const_cast<SrcKey*>(sd->targets));
return sd;
}
std::string show() const {
return folly::sformat("{} cases", cases);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(cases),
std::hash<int32_t>()(spOffBCFromStackBase.offset),
std::hash<int32_t>()(spOffBCFromIRSP.offset)
);
}
bool equals(const JmpSwitchData& o) const {
if (cases != o.cases) return false;
if (spOffBCFromStackBase != o.spOffBCFromStackBase) return false;
if (spOffBCFromIRSP != o.spOffBCFromIRSP) return false;
for (int64_t i = 0; i < cases; i++) {
if (targets[i] != o.targets[i]) return false;
}
return true;
}
int32_t cases; // number of cases
SrcKey* targets; // srckeys for all targets
SBInvOffset spOffBCFromStackBase;
IRSPRelOffset spOffBCFromIRSP;
};
struct LdTVAuxData : IRExtraData {
explicit LdTVAuxData(int32_t v = -1) : valid(v) {}
std::string show() const {
return folly::sformat("{:x}", valid);
}
size_t stableHash() const {
return std::hash<int32_t>()(valid);
}
bool equals(const LdTVAuxData& o) const {
return valid == o.valid;
}
int32_t valid;
};
struct ReqBindJmpData : IRExtraData {
explicit ReqBindJmpData(const SrcKey& target,
SBInvOffset invSPOff,
IRSPRelOffset irSPOff)
: target(target)
, invSPOff(invSPOff)
, irSPOff(irSPOff)
{}
std::string show() const {
return folly::sformat(
"{}, SBInv {}, IRSP {}",
target.printableOffset(), invSPOff.offset, irSPOff.offset
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
SrcKey::StableHasher()(target),
std::hash<int32_t>()(invSPOff.offset),
std::hash<int32_t>()(irSPOff.offset)
);
}
bool equals(const ReqBindJmpData& o) const {
return target == o.target && invSPOff == o.invSPOff && irSPOff == o.irSPOff;
}
SrcKey target;
SBInvOffset invSPOff;
IRSPRelOffset irSPOff;
};
struct StFrameMetaData : IRExtraData {
std::string show() const {
return folly::to<std::string>(
callBCOff, ',',
asyncEagerReturn
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<Offset>()(callBCOff),
std::hash<bool>()(asyncEagerReturn)
);
}
bool equals(const StFrameMetaData& o) const {
return callBCOff == o.callBCOff && asyncEagerReturn == o.asyncEagerReturn;
}
Offset callBCOff;
bool isInlined;
bool asyncEagerReturn;
};
struct CallBuiltinData : IRExtraData {
explicit CallBuiltinData(IRSPRelOffset spOffset,
const Func* callee)
: spOffset(spOffset)
, callee{callee}
{}
std::string show() const {
return folly::to<std::string>(
spOffset.offset, ',',
callee->fullName()->data()
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(spOffset.offset),
callee->stableHash()
);
}
bool equals(const CallBuiltinData& o) const {
return spOffset == o.spOffset &&
callee == o.callee;
}
IRSPRelOffset spOffset; // offset from StkPtr to last passed arg
const Func* callee;
};
struct CallData : IRExtraData {
explicit CallData(IRSPRelOffset spOffset,
uint32_t numArgs,
uint32_t numOut,
Offset callOffset,
uint16_t genericsBitmap,
bool hasGenerics,
bool hasUnpack,
bool skipRepack,
bool dynamicCall,
bool asyncEagerReturn,
bool formingRegion)
: spOffset(spOffset)
, numArgs(numArgs)
, numOut(numOut)
, callOffset(callOffset)
, genericsBitmap(genericsBitmap)
, hasGenerics(hasGenerics)
, hasUnpack(hasUnpack)
, skipRepack(skipRepack)
, dynamicCall(dynamicCall)
, asyncEagerReturn(asyncEagerReturn)
, formingRegion(formingRegion)
{}
std::string show() const {
return folly::to<std::string>(
spOffset.offset, ',', numArgs, ',', numOut, ',', callOffset,
hasGenerics
? folly::sformat(",hasGenerics({})", genericsBitmap)
: std::string{},
hasUnpack ? ",unpack" : "",
skipRepack ? ",skipRepack" : "",
dynamicCall ? ",dynamicCall" : "",
asyncEagerReturn ? ",asyncEagerReturn" : "",
formingRegion ? ",formingRegion" : ""
);
}
uint32_t numInputs() const {
return numArgs + (hasUnpack ? 1 : 0) + (hasGenerics ? 1 : 0);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(spOffset.offset),
std::hash<uint32_t>()(numArgs),
std::hash<uint32_t>()(numOut),
std::hash<Offset>()(callOffset),
std::hash<uint16_t>()(genericsBitmap),
std::hash<uint8_t>()(
hasGenerics << 5 |
hasUnpack << 4 |
skipRepack << 3 |
dynamicCall << 2 |
asyncEagerReturn << 1 |
formingRegion
)
);
}
bool equals(const CallData& o) const {
return spOffset == o.spOffset &&
numArgs == o.numArgs &&
numOut == o.numOut &&
callOffset == o.callOffset &&
genericsBitmap == o.genericsBitmap &&
hasGenerics == o.hasGenerics &&
hasUnpack == o.hasUnpack &&
skipRepack == o.skipRepack &&
dynamicCall == o.dynamicCall &&
asyncEagerReturn == o.asyncEagerReturn &&
formingRegion == o.formingRegion;
}
IRSPRelOffset spOffset; // offset from StkPtr to bottom of call's ActRec+args
uint32_t numArgs;
uint32_t numOut; // number of values returned via stack from the callee
Offset callOffset; // offset from func->base()
uint16_t genericsBitmap;
bool hasGenerics;
bool hasUnpack;
bool skipRepack;
bool dynamicCall;
bool asyncEagerReturn;
bool formingRegion;
};
struct RetCtrlData : IRExtraData {
explicit RetCtrlData(IRSPRelOffset offset, bool suspendingResumed,
AuxUnion aux)
: offset(offset)
, suspendingResumed(suspendingResumed)
, aux(aux)
{}
std::string show() const {
return folly::to<std::string>(
offset.offset,
suspendingResumed ? ",suspendingResumed" : ""
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<bool>()(suspendingResumed),
std::hash<uint32_t>()(aux.u_raw)
);
}
bool equals(const RetCtrlData& o) const {
return offset == o.offset && suspendingResumed == o.suspendingResumed &&
aux.u_raw == o.aux.u_raw;
}
// Adjustment we need to make to the stack pointer (for cross-tracelet ABI
// purposes) before returning.
IRSPRelOffset offset;
// Indicates that the current resumable frame is being suspended without
// decrefing locals. Used by refcount optimizer.
bool suspendingResumed;
// TV aux value to attach to the function's return value.
AuxUnion aux;
};
/*
* Name of a class constant in a known class
*/
struct ClsCnsName : IRExtraData {
explicit ClsCnsName(const StringData* cls, const StringData* cns)
: clsName(cls)
, cnsName(cns)
{}
std::string show() const {
return folly::to<std::string>(clsName->data(), "::", cnsName->data());
}
size_t stableHash() const {
return folly::hash::hash_combine(
clsName->hashStatic(),
cnsName->hashStatic()
);
}
bool equals(const ClsCnsName& o) const {
return clsName == o.clsName &&
cnsName == o.cnsName;
}
const StringData* clsName;
const StringData* cnsName;
};
/*
* Name and slot of a class constant.
*/
struct ClsCnsSlotData : IRExtraData {
explicit ClsCnsSlotData(const StringData* cns, Slot s)
: cnsName(cns)
, slot(s)
{}
std::string show() const {
return folly::sformat("{},{}", cnsName, slot);
}
size_t stableHash() const {
return folly::hash::hash_combine(
cnsName->hashStatic(),
std::hash<Slot>()(slot)
);
}
bool equals(const ClsCnsSlotData& o) const {
return cnsName == o.cnsName && slot == o.slot;
}
const StringData* cnsName;
Slot slot;
};
/*
* Name and handle of profiled class constant
*/
struct ProfileSubClsCnsData : IRExtraData {
explicit ProfileSubClsCnsData(const StringData* cns, rds::Handle h)
: cnsName(cns)
, handle(h)
{}
std::string show() const {
return folly::to<std::string>("<cls>::", cnsName->data());
}
size_t stableHash() const {
auto const sym = rds::reverseLink(handle);
return folly::hash::hash_combine(
cnsName->hashStatic(),
sym ? rds::symbol_stable_hash(*sym) : 0
);
}
bool equals(const ProfileSubClsCnsData& o) const {
return cnsName == o.cnsName && handle == o.handle;
}
const StringData* cnsName;
rds::Handle handle;
};
struct FuncNameData : IRExtraData {
FuncNameData(const StringData* name, const Class* context)
: name(name)
, context(context)
{}
std::string show() const {
return folly::to<std::string>(
name->data(), ",", context ? context->name()->data() : "{no context}");
}
size_t hash() const { return name->hash(); }
size_t stableHash() const {
return folly::hash::hash_combine(
name->hash(),
context ? context->stableHash() : 0
);
}
bool equals(const FuncNameData& o) const {
return name == o.name && context == o.context;
}
const StringData* name;
const Class* context;
};
/*
* Offset and stack deltas for InterpOne.
*/
struct InterpOneData : IRExtraData {
struct LocalType {
explicit LocalType(uint32_t id = 0, Type type = TBottom)
: id(id)
, type(type)
{}
uint32_t id;
Type type;
};
explicit InterpOneData(IRSPRelOffset spOffset)
: spOffset(spOffset)
, nChangedLocals(0)
, changedLocals(nullptr)
, smashesAllLocals(false)
{}
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
std::hash<int32_t>()(spOffset.offset),
std::hash<Offset>()(bcOff),
std::hash<int64_t>()(cellsPopped),
std::hash<int64_t>()(cellsPushed),
std::hash<Op>()(opcode),
std::hash<uint32_t>()(nChangedLocals),
std::hash<bool>()(smashesAllLocals)
);
for (uint32_t i = 0; i < nChangedLocals; i++) {
hash = folly::hash::hash_combine(
hash,
changedLocals[i].id,
changedLocals[i].type.stableHash()
);
}
return hash;
}
bool equals(const InterpOneData& o) const {
if (spOffset == o.spOffset &&
bcOff == o.bcOff &&
cellsPopped == o.cellsPopped &&
cellsPushed == o.cellsPushed &&
opcode == o.opcode &&
nChangedLocals == o.nChangedLocals &&
smashesAllLocals == o.smashesAllLocals) {
for (uint32_t i = 0; i < nChangedLocals; i++) {
if (changedLocals[i].id != o.changedLocals[i].id) return false;
if (changedLocals[i].type != o.changedLocals[i].type) return false;
}
return true;
}
return false;
}
// Offset of the BC stack top relative to the current IR stack pointer.
IRSPRelOffset spOffset;
// Offset of the instruction to interpret, in the Unit indicated by the
// current Marker.
Offset bcOff;
// The number of eval stack cells consumed and produced by the instruction,
// respectively. Includes ActRecs.
int64_t cellsPopped;
int64_t cellsPushed;
// Opcode, in case we need to fix the stack differently. Some bytecode
// instructions modify things below the top of the stack.
Op opcode;
uint32_t nChangedLocals;
LocalType* changedLocals;
bool smashesAllLocals;
InterpOneData* clone(Arena& arena) const {
auto* id = new (arena) InterpOneData(spOffset);
id->bcOff = bcOff;
id->cellsPopped = cellsPopped;
id->cellsPushed = cellsPushed;
id->opcode = opcode;
id->nChangedLocals = nChangedLocals;
id->changedLocals = new (arena) LocalType[nChangedLocals];
id->smashesAllLocals = smashesAllLocals;
std::copy(changedLocals, changedLocals + nChangedLocals, id->changedLocals);
return id;
}
std::string show() const {
auto ret = folly::sformat(
"{}: spOff:{}, bcOff:{}, popped:{}, pushed:{}",
opcodeToName(opcode),
spOffset.offset,
bcOff,
cellsPopped,
cellsPushed
);
assertx(!smashesAllLocals || !nChangedLocals);
if (smashesAllLocals) ret += ", smashes all locals";
if (nChangedLocals) {
for (auto i = 0; i < nChangedLocals; ++i) {
ret += folly::sformat(", Local {} -> {}",
changedLocals[i].id,
changedLocals[i].type);
}
}
return ret;
}
};
struct RBEntryData : IRExtraData {
RBEntryData(Trace::RingBufferType t, SrcKey sk)
: type(t)
, sk(sk)
{}
std::string show() const {
return folly::sformat("{}: {}", ringbufferName(type), showShort(sk));
}
size_t stableHash() const {
return std::hash<Trace::RingBufferType>()(type) ^
SrcKey::StableHasher()(sk);
}
bool equals(const RBEntryData& o) const {
return type == o.type && sk == o.sk;
}
Trace::RingBufferType type;
SrcKey sk;
};
struct RBMsgData : IRExtraData {
RBMsgData(Trace::RingBufferType t, const StringData* msg)
: type(t)
, msg(msg)
{
assertx(msg->isStatic());
}
std::string show() const {
return folly::sformat("{}: {}", ringbufferName(type), msg->data());
}
size_t stableHash() const {
return std::hash<Trace::RingBufferType>()(type) ^ msg->hash();
}
bool equals(const RBMsgData& o) const {
return type == o.type && msg->equal(o.msg);
}
Trace::RingBufferType type;
const StringData* msg;
};
struct ClassKindData : IRExtraData {
explicit ClassKindData(ClassKind kind): kind(uint32_t(kind)) {}
std::string show() const {
switch (static_cast<ClassKind>(kind)) {
case ClassKind::Class: return "cls";
case ClassKind::Interface: return "interface";
case ClassKind::Trait: return "trait";
case ClassKind::Enum: return "enum";
}
not_reached();
}
size_t stableHash() const {
return std::hash<uint32_t>()(kind);
}
bool equals(const ClassKindData& o) const {
return kind == o.kind;
}
uint32_t kind; // ... allows for direct usage in native_call
};
struct NewStructData : IRExtraData {
std::string show() const;
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<uint32_t>()(numKeys)
);
for (uint32_t i = 0; i < numKeys; i++) {
hash = folly::hash::hash_combine(
hash,
keys[i]->hashStatic()
);
}
return hash;
}
bool equals(const NewStructData& o) const {
if (offset != o.offset) return false;
if (numKeys != o.numKeys) return false;
for (uint32_t i = 0; i < numKeys; i++) {
if (keys[i] != o.keys[i]) return false;
}
return true;
}
IRSPRelOffset offset;
uint32_t numKeys;
StringData** keys;
};
struct AllocInitROMData : IRExtraData {
std::string show() const {
return ""; // TODO
}
size_t stableHash() const {
return size; // TODO(michaelofarrell): fixme
}
bool equals(const AllocInitROMData& o) const {
return rom == o.rom && size == o.size;
}
const uint8_t* rom;
size_t size;
};
struct ArrayLayoutData : IRExtraData {
explicit ArrayLayoutData(ArrayLayout layout) : layout(layout) {}
std::string show() const { return layout.describe(); }
size_t stableHash() const { return layout.toUint16(); }
bool equals(const ArrayLayoutData& o) const { return layout == o.layout; }
ArrayLayout layout;
};
struct NewBespokeStructData : IRExtraData {
NewBespokeStructData(ArrayLayout layout, IRSPRelOffset offset,
uint32_t numSlots, Slot* slots)
: layout(layout), offset(offset), numSlots(numSlots), slots(slots) {}
std::string show() const;
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
std::hash<uint16_t>()(layout.toUint16()),
std::hash<int32_t>()(offset.offset),
std::hash<uint32_t>()(numSlots)
);
for (auto i = 0; i < numSlots; i++) {
hash = folly::hash::hash_combine(hash, slots[i]);
}
return hash;
}
bool equals(const NewBespokeStructData& o) const {
if (layout != o.layout) return false;
if (offset != o.offset) return false;
if (numSlots != o.numSlots) return false;
for (auto i = 0; i < numSlots; i++) {
if (slots[i] != o.slots[i]) return false;
}
return true;
}
ArrayLayout layout;
IRSPRelOffset offset;
uint32_t numSlots;
Slot* slots;
};
struct InitStructPositionsData : IRExtraData {
InitStructPositionsData(ArrayLayout layout, uint32_t numSlots, Slot* slots)
: layout(layout), numSlots(numSlots), slots(slots) {}
std::string show() const;
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
std::hash<uint16_t>()(layout.toUint16()),
std::hash<uint32_t>()(numSlots)
);
for (auto i = 0; i < numSlots; i++) {
hash = folly::hash::hash_combine(hash, slots[i]);
}
return hash;
}
bool equals(const InitStructPositionsData& o) const {
if (layout != o.layout) return false;
if (numSlots != o.numSlots) return false;
for (auto i = 0; i < numSlots; i++) {
if (slots[i] != o.slots[i]) return false;
}
return true;
}
ArrayLayout layout;
uint32_t numSlots;
Slot* slots;
};
struct VanillaVecData : IRExtraData {
explicit VanillaVecData(uint32_t size) : size(size) {}
std::string show() const { return folly::format("{}", size).str(); }
size_t stableHash() const {
return std::hash<uint32_t>()(size);
}
bool equals(const VanillaVecData& o) const {
return size == o.size;
}
uint32_t size;
};
struct InitVanillaVecLoopData : IRExtraData {
explicit InitVanillaVecLoopData(IRSPRelOffset offset, uint32_t size)
: offset(offset)
, size(size)
{}
std::string show() const {
return folly::format("{},{}", offset.offset, size).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<uint32_t>()(size)
);
}
bool equals(const InitVanillaVecLoopData& o) const {
return offset == o.offset && size == o.size;
}
IRSPRelOffset offset;
uint32_t size;
};
struct CreateAAWHData : IRExtraData {
explicit CreateAAWHData(uint32_t first, uint32_t count)
: first(first)
, count(count)
{}
std::string show() const {
return folly::format("{},{}", first, count).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(first),
std::hash<uint32_t>()(count)
);
}
bool equals(const CreateAAWHData& o) const {
return first == o.first && count == o.count;
}
uint32_t first;
uint32_t count;
};
struct CountWHNotDoneData : IRExtraData {
explicit CountWHNotDoneData(uint32_t first, uint32_t count)
: first(first)
, count(count)
{}
std::string show() const {
return folly::format("{},{}", first, count).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(first),
std::hash<uint32_t>()(count)
);
}
bool equals(const CountWHNotDoneData& o) const {
return first == o.first && count == o.count;
}
uint32_t first;
uint32_t count;
};
struct NewKeysetArrayData : IRExtraData {
explicit NewKeysetArrayData(IRSPRelOffset offset, uint32_t size)
: offset(offset)
, size(size)
{}
std::string show() const {
return folly::format("{},{}", offset.offset, size).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<uint32_t>()(size)
);
}
bool equals(const NewKeysetArrayData& o) const {
return offset == o.offset && size == o.size;
}
IRSPRelOffset offset;
uint32_t size;
};
struct MemoValueStaticData : IRExtraData {
explicit MemoValueStaticData(const Func* func,
Optional<bool> asyncEager,
bool loadAux)
: func{func}
, asyncEager{asyncEager}
, loadAux{loadAux} {}
std::string show() const {
return folly::sformat(
"{},{},{}",
func->fullName()->toCppString(),
asyncEager ? folly::to<std::string>(*asyncEager) : "-",
loadAux
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<Optional<bool>>()(asyncEager),
std::hash<bool>()(loadAux)
);
}
bool equals(const MemoValueStaticData& o) const {
return func == o.func && asyncEager == o.asyncEager && loadAux == o.loadAux;
}
const Func* func;
Optional<bool> asyncEager;
bool loadAux;
};
struct MemoValueInstanceData : IRExtraData {
explicit MemoValueInstanceData(Slot slot,
const Func* func,
Optional<bool> asyncEager,
bool loadAux)
: slot{slot}
, func{func}
, asyncEager{asyncEager}
, loadAux{loadAux} {}
std::string show() const {
return folly::sformat(
"{},{},{},{}",
slot,
func->fullName(),
asyncEager ? folly::to<std::string>(*asyncEager) : "-",
loadAux
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<Slot>()(slot),
func->stableHash(),
std::hash<Optional<bool>>()(asyncEager),
std::hash<bool>()(loadAux)
);
}
bool equals(const MemoValueInstanceData& o) const {
return slot == o.slot && func == o.func &&
asyncEager == o.asyncEager && loadAux == o.loadAux;
}
Slot slot;
const Func* func;
Optional<bool> asyncEager;
bool loadAux;
};
struct MemoCacheStaticData : IRExtraData {
MemoCacheStaticData(const Func* func,
LocalRange keys,
const bool* types,
Optional<bool> asyncEager,
bool loadAux)
: func{func}
, keys{keys}
, types{types}
, asyncEager{asyncEager}
, loadAux{loadAux} {}
MemoCacheStaticData* clone(Arena& arena) const {
auto p =
new (arena) MemoCacheStaticData(func, keys, types, asyncEager, loadAux);
auto tmp = new (arena) bool[keys.count];
std::copy(types, types + keys.count, tmp);
p->types = tmp;
return p;
}
std::string show() const {
std::string ret;
ret += folly::sformat("{},{}", func->fullName(), HPHP::show(keys));
if (keys.count > 0) {
ret += ",<";
for (auto i = 0; i < keys.count; ++i) {
if (i > 0) ret += ",";
ret += folly::sformat("{}", types[i] ? "string" : "int");
}
ret += ">";
}
return ret;
}
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
func->stableHash(),
std::hash<Optional<bool>>()(asyncEager),
std::hash<uint32_t>()(keys.first),
std::hash<uint32_t>()(keys.count),
std::hash<bool>()(loadAux)
);
for (auto i = 0; i < keys.count; i++) {
hash = folly::hash::hash_combine(
hash,
std::hash<bool>()(types[i])
);
}
return hash;
}
bool equals(const MemoCacheStaticData& o) const {
if (func == o.func && asyncEager == o.asyncEager && loadAux == o.loadAux &&
keys.first == o.keys.first && keys.count == o.keys.count) {
for (auto i = 0; i < keys.count; i++) {
if (types[i] != o.types[i]) return false;
}
return true;
}
return false;
}
const Func* func;
LocalRange keys;
const bool* types;
Optional<bool> asyncEager;
bool loadAux;
};
struct MemoCacheInstanceData : IRExtraData {
MemoCacheInstanceData(Slot slot,
LocalRange keys,
const bool* types,
const Func* func,
bool shared,
Optional<bool> asyncEager,
bool loadAux)
: slot{slot}
, keys{keys}
, types{types}
, func{func}
, shared{shared}
, asyncEager{asyncEager}
, loadAux{loadAux} {}
MemoCacheInstanceData* clone(Arena& arena) const {
auto p = new (arena) MemoCacheInstanceData(
slot, keys, types, func, shared, asyncEager, loadAux
);
auto tmp = new (arena) bool[keys.count];
std::copy(types, types + keys.count, tmp);
p->types = tmp;
return p;
}
std::string show() const {
return folly::sformat(
"{},{},{}<{}>,{}",
slot,
func->fullName(),
HPHP::show(keys),
[&]{
using namespace folly::gen;
return range<uint32_t>(0, keys.count)
| map([this] (uint32_t i) { return types[i] ? "string" : "int"; })
| unsplit<std::string>(",");
}(),
shared ? "shared" : "non-shared"
);
}
size_t stableHash() const {
auto hash = folly::hash::hash_combine(
std::hash<Slot>()(slot),
func->stableHash(),
std::hash<Optional<bool>>()(asyncEager),
std::hash<uint32_t>()(keys.first),
std::hash<uint32_t>()(keys.count),
std::hash<bool>()(loadAux),
std::hash<bool>()(shared)
);
for (auto i = 0; i < keys.count; i++) {
hash = folly::hash::hash_combine(
hash,
std::hash<bool>()(types[i])
);
}
return hash;
}
bool equals(const MemoCacheInstanceData& o) const {
if (slot == o.slot && func == o.func && asyncEager == o.asyncEager &&
loadAux == o.loadAux && keys.first == o.keys.first &&
keys.count == o.keys.count && shared == o.shared) {
for (auto i = 0; i < keys.count; i++) {
if (types[i] != o.types[i]) return false;
}
return true;
}
return false;
}
Slot slot;
LocalRange keys;
const bool* types;
const Func* func;
bool shared;
Optional<bool> asyncEager;
bool loadAux;
};
struct MOpModeData : IRExtraData {
explicit MOpModeData(MOpMode mode) : mode{mode} {}
std::string show() const { return subopToName(mode); }
size_t stableHash() const {
return std::hash<MOpMode>()(mode);
}
bool equals(const MOpModeData& o) const {
return mode == o.mode;
}
MOpMode mode;
};
struct PropData : IRExtraData {
explicit PropData(MOpMode mode, ReadonlyOp op) : mode{mode}, op(op) {}
std::string show() const {
return fmt::format("{} {}", subopToName(mode), subopToName(op));
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<MOpMode>()(mode),
std::hash<ReadonlyOp>()(op)
);
}
bool equals(const PropData& o) const {
return mode == o.mode && op == o.op;
}
MOpMode mode;
ReadonlyOp op;
};
struct ReadonlyData : IRExtraData {
explicit ReadonlyData(ReadonlyOp op) : op(op) {}
std::string show() const { return subopToName(op); }
size_t hash() const {
return std::hash<ReadonlyOp>()(op);
}
size_t stableHash() const {
return std::hash<ReadonlyOp>()(op);
}
bool equals(const ReadonlyData& o) const {
return op == o.op;
}
ReadonlyOp op;
};
struct SetOpData : IRExtraData {
explicit SetOpData(SetOpOp op) : op(op) {}
std::string show() const { return subopToName(op); }
size_t stableHash() const {
return std::hash<SetOpOp>()(op);
}
bool equals(const SetOpData& o) const {
return op == o.op;
}
SetOpOp op;
};
struct DecRefData : IRExtraData {
explicit DecRefData(int locId = -1) : locId(locId) {}
std::string show() const {
return locId != -1 ? folly::to<std::string>("Loc", locId) : "-";
}
size_t stableHash() const {
return std::hash<int>()(locId);
}
bool equals(const DecRefData& o) const {
return locId == o.locId;
}
int locId; // If a known local, this has its id; -1 otherwise.
};
struct IncDecData : IRExtraData {
explicit IncDecData(IncDecOp op) : op(op) {}
std::string show() const { return subopToName(op); }
size_t stableHash() const {
return std::hash<IncDecOp>()(op);
}
bool equals(const IncDecData& o) const {
return op == o.op;
}
IncDecOp op;
};
struct SuspendOffset : IRExtraData {
explicit SuspendOffset(Offset off) : off(off) {}
std::string show() const { return folly::to<std::string>(off); }
size_t stableHash() const {
return std::hash<Offset>()(off);
}
bool equals(const SuspendOffset& o) const {
return off == o.off;
}
Offset off;
};
struct GeneratorState : IRExtraData {
explicit GeneratorState(BaseGenerator::State state) : state(state) {}
std::string show() const {
using U = std::underlying_type<BaseGenerator::State>::type;
return folly::to<std::string>(static_cast<U>(state));
}
size_t stableHash() const {
return std::hash<BaseGenerator::State>()(state);
}
bool equals(const GeneratorState& o) const {
return state == o.state;
}
BaseGenerator::State state;
};
struct ContEnterData : IRExtraData {
explicit ContEnterData(IRSPRelOffset spOffset, Offset callBCOffset,
bool isAsync)
: spOffset(spOffset)
, callBCOffset(callBCOffset)
, isAsync(isAsync)
{}
std::string show() const {
return folly::to<std::string>(spOffset.offset, ',', callBCOffset,
isAsync ? ",async" : "");
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(spOffset.offset),
std::hash<Offset>()(callBCOffset),
std::hash<bool>()(isAsync)
);
}
bool equals(const ContEnterData& o) const {
return spOffset == o.spOffset && callBCOffset == o.callBCOffset &&
isAsync && o.isAsync;
}
IRSPRelOffset spOffset;
Offset callBCOffset;
bool isAsync;
};
struct NewColData : IRExtraData {
explicit NewColData(CollectionType itype)
: type(itype)
{}
std::string show() const {
return collections::typeToString(type)->toCppString();
}
size_t stableHash() const {
return std::hash<CollectionType>()(type);
}
bool equals(const NewColData& o) const {
return type == o.type;
}
CollectionType type;
};
struct LocalIdRange : IRExtraData {
LocalIdRange(uint32_t start, uint32_t end)
: start(start)
, end(end)
{}
std::string show() const {
return folly::format("[{}, {})", start, end).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(start),
std::hash<uint32_t>()(end)
);
}
bool equals(const LocalIdRange& o) const {
return start == o.start && end == o.end;
}
uint32_t start, end;
};
struct StackRange : IRExtraData {
StackRange(IRSPRelOffset start, uint32_t count)
: start(start)
, count(count)
{}
std::string show() const {
return folly::sformat("[{}, {})", start.offset, start.offset + count);
}
size_t stableHash() const {
return folly::hash::hash_combine(std::hash<int32_t>()(start.offset),
std::hash<int32_t>()(count));
}
bool equals(const StackRange& o) const {
return start == o.start && count == o.count;
}
IRSPRelOffset start;
uint32_t count;
};
struct FuncEntryData : IRExtraData {
FuncEntryData(const Func* func, uint32_t argc)
: func(func)
, argc(argc)
{}
std::string show() const {
return folly::format(
"{}({} args)",
func->fullName(),
argc
).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<uint32_t>()(argc)
);
}
bool equals(const FuncEntryData& o) const {
return func == o.func && argc == o.argc;
}
const Func* func;
uint32_t argc;
};
struct BoolVecArgsData : IRExtraData {
BoolVecArgsData(uint32_t numArgs, const uint8_t* args)
: numArgs(numArgs)
, args(reinterpret_cast<uintptr_t>(args))
{}
std::string show() const {
return HPHP::show(numArgs, reinterpret_cast<const uint8_t*>(args));
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<uint32_t>()(numArgs),
std::hash<uintptr_t>()(args)
);
}
bool equals(const BoolVecArgsData& o) const {
return numArgs == o.numArgs && args == o.args;
}
uint32_t numArgs;
uintptr_t args;
};
struct ProfileCallTargetData : IRExtraData {
explicit ProfileCallTargetData(rds::Handle handle)
: handle(handle)
{}
std::string show() const {
return folly::to<std::string>(handle);
}
size_t stableHash() const {
auto const sym = rds::reverseLink(handle);
if (!sym) return 0;
return rds::symbol_stable_hash(*sym);
}
bool equals(const ProfileCallTargetData& o) const {
return handle == o.handle;
}
rds::Handle handle;
};
struct BeginInliningData : IRExtraData {
BeginInliningData(IRSPRelOffset offset, const Func* func, unsigned depth,
SrcKey returnSk, IRSPRelOffset sbOffset,
SBInvOffset returnSPOff, int cost, int numArgs)
: spOffset(offset)
, func(func)
, depth(depth)
, returnSk(returnSk)
, sbOffset(sbOffset)
, returnSPOff(returnSPOff)
, cost(cost)
, numArgs(numArgs)
{}
std::string show() const {
return folly::to<std::string>("IRSPOff ", spOffset.offset,
" FUNC ", func->fullName()->data());
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(spOffset.offset),
func->stableHash(),
std::hash<unsigned>()(depth),
SrcKey::StableHasher()(returnSk),
std::hash<int32_t>()(sbOffset.offset),
std::hash<int32_t>()(returnSPOff.offset),
std::hash<int>()(cost),
std::hash<int>()(numArgs)
);
}
bool equals(const BeginInliningData& o) const {
return
spOffset == o.spOffset && func == o.func && depth == o.depth &&
returnSk == o.returnSk && sbOffset == o.sbOffset &&
returnSPOff == o.returnSPOff && cost == o.cost && numArgs == o.numArgs;
}
IRSPRelOffset spOffset; // offset from SP to the bottom of callee's ActRec
const Func* func; // inlined function
unsigned depth; // inlining depth
SrcKey returnSk; // return SrcKey
IRSPRelOffset sbOffset; // offset from SP to the callee's stack base
SBInvOffset returnSPOff; // offset from caller's stack base to return slot
int cost;
int numArgs;
};
struct ParamData : IRExtraData {
explicit ParamData(int32_t paramId) : paramId(paramId) {}
std::string show() const {
return folly::to<std::string>(paramId);
}
size_t stableHash() const { return std::hash<int32_t>()(paramId); }
bool equals(const ParamData& o) const {
return paramId == o.paramId;
}
int32_t paramId;
};
struct FuncParamData : IRExtraData {
explicit FuncParamData(const Func* func, int32_t paramId)
: func(func)
, paramId(paramId) {}
std::string show() const {
return folly::to<std::string>(func->fullName()->data(), ":", paramId);
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<int32_t>()(paramId)
);
}
bool equals(const FuncParamData& o) const {
return func == o.func && paramId == o.paramId;
}
const Func* func;
int32_t paramId;
};
struct FuncParamWithTCData : IRExtraData {
explicit FuncParamWithTCData(const Func* func, int32_t paramId,
const TypeConstraint* tc)
: func(func)
, paramId(paramId)
, tc(tc) {}
std::string show() const {
return folly::to<std::string>(
func->fullName()->data(), ":", paramId, ":", tc->displayName());
}
size_t stableHash() const {
return folly::hash::hash_combine(
func->stableHash(),
std::hash<int32_t>()(paramId),
std::hash<std::string>()(tc->fullName()) // Not great but hey its easy.
);
}
bool equals(const FuncParamWithTCData& o) const {
return func == o.func && paramId == o.paramId && *tc == *o.tc;
}
const Func* func;
int32_t paramId;
union {
const TypeConstraint* tc;
uintptr_t tcAsInt;
};
};
struct TypeConstraintData : IRExtraData {
explicit TypeConstraintData(const TypeConstraint* tc)
: tc(tc) {}
std::string show() const { return tc->displayName(); }
size_t stableHash() const {
// Not great but easy.
return std::hash<std::string>()(tc->fullName());
}
bool equals(const TypeConstraintData& o) const {
return *tc == *o.tc;
}
const TypeConstraint* tc;
};
struct ArrayGetExceptionData : IRExtraData {
explicit ArrayGetExceptionData(bool isInOut) : isInOut(isInOut) {}
std::string show() const {
return isInOut ? "inout" : "none";
}
size_t stableHash() const {
return std::hash<bool>()(isInOut);
}
bool equals(const ArrayGetExceptionData& o) const {
return isInOut == o.isInOut;
}
bool isInOut;
};
struct AssertReason : IRExtraData {
explicit AssertReason(Reason r) : reason{r.file, r.line} {}
std::string show() const {
return jit::show(reason);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<const char*>()(reason.file),
std::hash<unsigned>()(reason.line)
);
}
bool equals(const AssertReason& o) const {
return reason == o.reason;
}
Reason reason;
};
#define ASSERT_REASON AssertReason{Reason{__FILE__, __LINE__}}
struct EndCatchData : IRExtraData {
enum class CatchMode {
UnwindOnly,
SideExit,
LocalsDecRefd
};
enum class FrameMode { Phplogue, Stublogue };
enum class Teardown { NA, None, Full, OnlyThis };
explicit EndCatchData(IRSPRelOffset offset, CatchMode mode,
FrameMode stublogue, Teardown teardown)
: offset{offset}
, mode{mode}
, stublogue{stublogue}
, teardown{teardown}
{}
std::string show() const {
return folly::to<std::string>(
"IRSPOff ", offset.offset, ",",
mode == CatchMode::UnwindOnly ? "UnwindOnly" :
mode == CatchMode::SideExit ? "SideExit" : "LocalsDecRefd", ",",
stublogue == FrameMode::Stublogue ? "Stublogue" : "Phplogue", ",",
teardown == Teardown::NA ? "NA" :
teardown == Teardown::None ? "None" :
teardown == Teardown::Full ? "Full" : "OnlyThis");
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<CatchMode>()(mode),
std::hash<FrameMode>()(stublogue),
std::hash<Teardown>()(teardown)
);
}
bool equals(const EndCatchData& o) const {
return offset == o.offset && mode == o.mode && stublogue == o.stublogue &&
teardown == o.teardown;
}
IRSPRelOffset offset;
CatchMode mode;
FrameMode stublogue;
Teardown teardown;
};
struct EnterTCUnwindData : IRExtraData {
explicit EnterTCUnwindData(IRSPRelOffset offset, bool teardown)
: offset{offset}, teardown{teardown} {}
std::string show() const {
return folly::to<std::string>(
"IRSPOff ", offset.offset, ",",
teardown ? "" : "no-", "teardown"
);
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<int32_t>()(offset.offset),
std::hash<bool>()(teardown)
);
}
bool equals(const EnterTCUnwindData& o) const {
return offset == o.offset && teardown == o.teardown;
}
IRSPRelOffset offset;
bool teardown;
};
/*
* Func/Class/Prop attributes
*/
struct AttrData : IRExtraData {
explicit AttrData(Attr attr) : attr(static_cast<int32_t>(attr)) {}
std::string show() const {
return folly::format("{}", attr).str();
}
size_t stableHash() const {
return std::hash<int32_t>()(attr);
}
size_t hash() const {
return std::hash<int32_t>()(attr);
}
bool equals(const AttrData& o) const {
return attr == o.attr;
}
int32_t attr;
};
struct MethCallerData : IRExtraData {
explicit MethCallerData(bool isCls) : isCls(isCls) {}
std::string show() const {
return folly::format("{}", isCls).str();
}
size_t stableHash() const {
return std::hash<bool>()(isCls);
}
bool equals(const MethCallerData& o) const {
return isCls == o.isCls;
}
bool isCls;
};
struct LoggingProfileData : IRExtraData {
LoggingProfileData(bespoke::LoggingProfile* profile, bool isStatic)
: profile(profile)
, isStatic(isStatic)
{}
std::string show() const {
// profile->source is already printed in the instruction's marker.
return folly::sformat("{}", reinterpret_cast<void*>(profile));
}
size_t stableHash() const;
bool equals(const LoggingProfileData& o) const {
return profile == o.profile;
}
bespoke::LoggingProfile* profile;
bool isStatic; // Whether the output is guaranteed to be static
};
struct SinkProfileData : IRExtraData {
explicit SinkProfileData(bespoke::SinkProfile* profile)
: profile(profile)
{}
std::string show() const {
// profile->sink is already printed in the instruction's marker.
return folly::sformat("{}", reinterpret_cast<void*>(profile));
}
size_t stableHash() const;
bool equals(const SinkProfileData& o) const {
return profile == o.profile;
}
bespoke::SinkProfile* profile;
};
struct BespokeGetData : IRExtraData {
enum class KeyState { Present, Unknown };
explicit BespokeGetData(KeyState state) : state(state) {}
std::string show() const {
switch (state) {
case KeyState::Present: return "Present";
case KeyState::Unknown: return "Unknown";
}
always_assert(false);
}
size_t hash() const {
return stableHash();
}
size_t stableHash() const {
return std::hash<KeyState>()(state);
}
bool equals(const BespokeGetData& o) const {
return state == o.state;
}
KeyState state;
};
struct ConvNoticeData : IRExtraData {
explicit ConvNoticeData(ConvNoticeLevel l = ConvNoticeLevel::None,
const StringData* r = nullptr)
: level(l), reason(r) {}
std::string show() const {
assertx(level == ConvNoticeLevel::None || (reason != nullptr && reason->isStatic()));
const auto reason_str = reason ? folly::format(" for {}", reason).str() : "";
return folly::format("{}{}", convOpToName(level), reason_str).str();
}
size_t stableHash() const {
return folly::hash::hash_combine(
std::hash<ConvNoticeLevel>()(level),
std::hash<const StringData*>()(reason)
);
}
bool equals(const ConvNoticeData& o) const {
// can use pointer equality bc reason is always a StaticString
return level == o.level && reason == o.reason;
}
ConvNoticeLevel level;
union { const StringData* reason; int64_t reasonIntVal; };
};
struct AliasClassData : IRExtraData {
explicit AliasClassData(AliasClass acls) : acls{acls} {}
std::string show() const { return jit::show(acls); }
bool equals(const AliasClassData& o) const {
return acls == o.acls;
}
AliasClass acls;
};
//////////////////////////////////////////////////////////////////////
#define X(op, data) \
template<> struct IRExtraDataType<op> { typedef data type; }; \
template<> struct OpHasExtraData<op> { enum { value = 1 }; }; \
static_assert(boost::has_trivial_destructor<data>::value, \
"IR extra data type must be trivially destructible")
X(DictIdx, SizeHintData);
X(LdBindAddr, LdBindAddrData);
X(ProfileSwitchDest, ProfileSwitchData);
X(JmpSwitchDest, JmpSwitchData);
X(LdSSwitchDest, LdSSwitchData);
X(CheckLoc, LocalId);
X(AssertLoc, LocalId);
X(LdLocAddr, LocalId);
X(LdLoc, LocalId);
X(LdClsInitElem, IndexData);
X(StClsInitElem, IndexData);
X(KillLoc, LocalId);
X(StLoc, LocalId);
X(StLocMeta, LocalId);
X(StLocRange, LocalIdRange);
X(AdvanceDictPtrIter, IterOffsetData);
X(AdvanceVecPtrIter, IterOffsetData);
X(StFrameFunc, FuncData);
X(CheckIter, IterTypeData);
X(StIterBase, IterId);
X(StIterType, IterTypeData);
X(StIterPos, IterId);
X(StIterEnd, IterId);
X(LdIterBase, IterId);
X(LdIterPos, IterId);
X(LdIterEnd, IterId);
X(KillIter, IterId);
X(IterFree, IterId);
X(IterInit, IterData);
X(IterInitK, IterData);
X(IterNext, IterData);
X(IterNextK, IterData);
X(LIterInit, IterData);
X(LIterInitK, IterData);
X(LIterNext, IterData);
X(LIterNextK, IterData);
X(ConstructInstance, ClassData);
X(ConstructClosure, ClassData);
X(InitProps, ClassData);
X(InitSProps, ClassData);
X(NewInstanceRaw, ClassData);
X(InitObjProps, ClassData);
X(InitObjMemoSlots, ClassData);
X(InstanceOfIfaceVtable, InstanceOfIfaceVtableData);
X(ResolveTypeStruct, ResolveTypeStructData);
X(ExtendsClass, ExtendsClassData);
X(CheckStk, IRSPRelOffsetData);
X(StStk, IRSPRelOffsetData);
X(StStkMeta, IRSPRelOffsetData);
X(StStkRange, StackRange);
X(StOutValue, IndexData);
X(LdOutAddr, IndexData);
X(AssertStk, IRSPRelOffsetData);
X(DefFP, DefFPData);
X(DefFrameRelSP, DefStackData);
X(DefRegSP, DefStackData);
X(LdStk, IRSPRelOffsetData);
X(LdStkAddr, IRSPRelOffsetData);
X(StFrameMeta, StFrameMetaData);
X(BeginInlining, BeginInliningData);
X(ReqBindJmp, ReqBindJmpData);
X(ReqInterpBBNoTranslate, ReqBindJmpData);
X(ReqRetranslate, IRSPRelOffsetData);
X(ReqRetranslateOpt, IRSPRelOffsetData);
X(CheckCold, TransIDData);
X(IncProfCounter, TransIDData);
X(IncCallCounter, FuncData);
X(LogArrayReach, SinkProfileData);
X(NewLoggingArray, LoggingProfileData);
X(BespokeGet, BespokeGetData);
X(DefFuncEntryFP, FuncData);
X(InitFrame, FuncData);
X(Call, CallData);
X(CallBuiltin, CallBuiltinData);
X(RetCtrl, RetCtrlData);
X(AsyncFuncRet, IRSPRelOffsetData);
X(AsyncFuncRetSlow, IRSPRelOffsetData);
X(AsyncGenRetR, IRSPRelOffsetData);
X(AsyncGenYieldR, IRSPRelOffsetData);
X(AsyncSwitchFast, IRSPRelOffsetData);
X(LookupClsMethodCache, ClsMethodData);
X(LdClsMethodCacheFunc, ClsMethodData);
X(LdClsMethodCacheCls, ClsMethodData);
X(LdClsMethodFCacheFunc, ClsMethodData);
X(LookupClsMethodFCache, ClsMethodData);
X(LdIfaceMethod, IfaceMethodData);
X(LdClsCns, ClsCnsName);
X(InitClsCns, ClsCnsName);
X(InitSubClsCns, ClsCnsSlotData);
X(LdSubClsCns, ClsCnsSlotData);
X(CheckSubClsCns, ClsCnsSlotData);
X(LdResolvedTypeCns, ClsCnsSlotData);
X(LdResolvedTypeCnsClsName, ClsCnsSlotData);
X(LdResolvedTypeCnsNoCheck, ClsCnsSlotData);
X(ProfileSubClsCns, ProfileSubClsCnsData);
X(LdFuncCached, FuncNameData);
X(LookupFuncCached, FuncNameData);
X(LdObjMethodS, FuncNameData);
X(LdObjMethodD, OptClassData);
X(ThrowMissingArg, FuncArgData);
X(RaiseTooManyArg, FuncData);
X(RaiseCoeffectsCallViolation, FuncData);
X(RaiseCoeffectsFunParamTypeViolation,
ParamData);
X(CheckInOutMismatch, BoolVecArgsData);
X(ThrowInOutMismatch, ParamData);
X(CheckReadonlyMismatch, BoolVecArgsData);
X(ThrowReadonlyMismatch, ParamData);
X(ThrowParameterWrongType, FuncArgTypeData);
X(CheckClsReifiedGenericMismatch,
ClassData);
X(IsFunReifiedGenericsMatched, FuncData);
X(IsTypeStruct, RDSHandleData);
X(InterpOne, InterpOneData);
X(InterpOneCF, InterpOneData);
X(StClosureArg, IndexData);
X(RBTraceEntry, RBEntryData);
X(RBTraceMsg, RBMsgData);
X(OODeclExists, ClassKindData);
X(NewStructDict, NewStructData);
X(AllocStructDict, NewStructData);
X(AllocInitROM, AllocInitROMData);
X(AllocBespokeStructDict, ArrayLayoutData);
X(InitStructPositions, InitStructPositionsData);
X(NewBespokeStructDict, NewBespokeStructData);
X(AllocVec, VanillaVecData);
X(NewKeysetArray, NewKeysetArrayData);
X(InitVecElemLoop, InitVanillaVecLoopData);
X(InitVecElem, IndexData);
X(InitDictElem, KeyedIndexData);
X(InitStructElem, KeyedIndexData);
X(CreateAAWH, CreateAAWHData);
X(CountWHNotDone, CountWHNotDoneData);
X(CheckDictOffset, IndexData);
X(CheckKeysetOffset, IndexData);
X(ProfileArrayCOW, RDSHandleData);
X(ProfileDictAccess, ArrayAccessProfileData);
X(ProfileKeysetAccess, ArrayAccessProfileData);
X(ProfileType, RDSHandleData);
X(ProfileCall, ProfileCallTargetData);
X(ProfileMethod, ProfileCallTargetData);
X(ProfileIsTypeStruct, RDSHandleData);
X(CheckRDSInitialized, RDSHandleData);
X(MarkRDSInitialized, RDSHandleData);
X(MarkRDSAccess, RDSHandleData);
X(LdRDSAddr, RDSHandleAndType);
X(LdInitRDSAddr, RDSHandleAndType);
X(LdTVFromRDS, TVInRDSHandleData);
X(StTVInRDS, TVInRDSHandleData);
X(BaseG, MOpModeData);
X(PropX, PropData);
X(PropQ, PropData);
X(PropDX, PropData);
X(ElemX, MOpModeData);
X(ElemDX, MOpModeData);
X(ElemUX, MOpModeData);
X(CGetProp, PropData);
X(CGetPropQ, PropData);
X(CGetElem, MOpModeData);
X(MemoGetStaticValue, MemoValueStaticData);
X(MemoSetStaticValue, MemoValueStaticData);
X(MemoGetStaticCache, MemoCacheStaticData);
X(MemoSetStaticCache, MemoCacheStaticData);
X(MemoGetLSBValue, MemoValueStaticData);
X(MemoSetLSBValue, MemoValueStaticData);
X(MemoGetLSBCache, MemoCacheStaticData);
X(MemoSetLSBCache, MemoCacheStaticData);
X(MemoGetInstanceValue, MemoValueInstanceData);
X(MemoSetInstanceValue, MemoValueInstanceData);
X(MemoGetInstanceCache, MemoCacheInstanceData);
X(MemoSetInstanceCache, MemoCacheInstanceData);
X(SetOpProp, SetOpData);
X(SetOpTV, SetOpData);
X(SetProp, ReadonlyData);
X(OutlineSetOp, SetOpData);
X(IncDecProp, IncDecData);
X(SetOpElem, SetOpData);
X(IncDecElem, IncDecData);
X(StArResumeAddr, SuspendOffset);
X(StContArState, GeneratorState);
X(ContEnter, ContEnterData);
X(LoadBCSP, IRSPRelOffsetData);
X(JmpSSwitchDest, IRSPRelOffsetData);
X(DbgTrashStk, IRSPRelOffsetData);
X(DbgTrashFrame, IRSPRelOffsetData);
X(DbgTraceCall, IRSPRelOffsetData);
X(LdPropAddr, IndexData);
X(LdInitPropAddr, IndexData);
X(NewCol, NewColData);
X(NewColFromArray, NewColData);
X(CheckSurpriseFlagsEnter, FuncEntryData);
X(CheckSurpriseAndStack, FuncEntryData);
X(ContCheckNext, IsAsyncData);
X(ContValid, IsAsyncData);
X(LdContResumeAddr, IsAsyncData);
X(LdContActRec, IsAsyncData);
X(DecRef, DecRefData);
X(DecRefNZ, DecRefData);
X(ProfileDecRef, DecRefData);
X(LdTVAux, LdTVAuxData);
X(DbgAssertRefCount, AssertReason);
X(Unreachable, AssertReason);
X(EndBlock, AssertReason);
X(VerifyParamCallable, FuncParamData);
X(VerifyParamCls, FuncParamWithTCData);
X(VerifyParamFail, FuncParamWithTCData);
X(VerifyParamFailHard, FuncParamWithTCData);
X(VerifyRetCallable, FuncParamData);
X(VerifyRetCls, FuncParamWithTCData);
X(VerifyRetFail, FuncParamWithTCData);
X(VerifyRetFailHard, FuncParamWithTCData);
X(VerifyReifiedLocalType, FuncParamData);
X(VerifyReifiedReturnType, FuncData);
X(VerifyPropCls, TypeConstraintData);
X(VerifyPropFail, TypeConstraintData);
X(VerifyPropFailHard, TypeConstraintData);
X(VerifyProp, TypeConstraintData);
X(VerifyPropCoerce, TypeConstraintData);
X(EndCatch, EndCatchData);
X(EnterTCUnwind, EnterTCUnwindData);
X(FuncHasAttr, AttrData);
X(ClassHasAttr, AttrData);
X(LdMethCallerName, MethCallerData);
X(LdUnitPerRequestFilepath, RDSHandleData);
X(ConvTVToStr, ConvNoticeData);
X(CheckFuncNeedsCoverage, FuncData);
X(RecordFuncCall, FuncData);
X(LdClsPropAddrOrNull, ReadonlyData);
X(LdClsPropAddrOrRaise, ReadonlyData);
X(LdMBase, AliasClassData);
X(ThrowMustBeEnclosedInReadonly,ClassData);
X(ThrowMustBeMutableException, ClassData);
X(ThrowMustBeReadonlyException, ClassData);
X(ThrowMustBeValueTypeException,ClassData);
#undef X
//////////////////////////////////////////////////////////////////////
template<bool hasExtra, Opcode opc, class T> struct AssertExtraTypes {
static void doassertx() {
assertx(!"called extra on an opcode without extra data");
}
static void doassert_same() {
assertx(!"called extra on an opcode without extra data");
}
};
template<Opcode opc, class T> struct AssertExtraTypes<true,opc,T> {
typedef typename IRExtraDataType<opc>::type ExtraType;
static void doassertx() {
if (!std::is_base_of<T,ExtraType>::value) {
assertx(!"extra<T> was called with an extra data "
"type that doesn't match the opcode type");
}
}
static void doassert_same() {
if (!std::is_same<T,ExtraType>::value) {
fprintf(stderr, "opcode = %s\n", opcodeName(opc)); \
assertx(!"extra<T> was called with an extra data type that "
"doesn't exactly match the opcode type");
}
}
};
// Asserts that Opcode opc has extradata and it is of type T, or a
// type derived from T.
template<class T> void assert_opcode_extra(Opcode opc) {
#define O(opcode, dstinfo, srcinfo, flags) \
case opcode: \
AssertExtraTypes< \
OpHasExtraData<opcode>::value,opcode,T \
>::doassertx(); \
break;
switch (opc) { IR_OPCODES default: not_reached(); }
#undef O
}
template<class T> void assert_opcode_extra_same(Opcode opc) {
#define O(opcode, dstinfo, srcinfo, flags) \
case opcode: \
AssertExtraTypes< \
OpHasExtraData<opcode>::value,opcode,T \
>::doassert_same(); \
break;
switch (opc) { IR_OPCODES default: not_reached(); }
#undef O
}
size_t hashExtra(Opcode opc, const IRExtraData* data);
size_t stableHashExtra(Opcode opc, const IRExtraData* data);
bool equalsExtra(Opcode opc, const IRExtraData* a, const IRExtraData* b);
IRExtraData* cloneExtra(Opcode opc, IRExtraData* data, Arena& a);
std::string showExtra(Opcode opc, const IRExtraData* data);
//////////////////////////////////////////////////////////////////////
}