Better reffiness checks
While debugging a sandbox crash, I spent some time looking at a huge sequnce of conditional masks, compares and branches that didnt seem to belong in the code I was debugging. Finally realized that it was a reffiness check. It looked way too complicated, so I investigated. Part of the problem was that we were avoiding a malloc in the case of a zero param function at the expense of an extra check. Instead, this diff always sets up at least 64 bits worth of m_refBitVec. But by using the space set aside for the pointer in Func::m_shared it avoids a malloc for any function with fewer than 65 arguments, and avoids the numParams check for the first 64 parameters. In addition, the existing code was spitting out a generic test for the guard condition - (mask & bits) == value - where mask and value are known constants. Since the most common case is that value == 0 (all the parameters are expected to be by value), we can usually omit the compare. In addition, since most functions only have a small number of parameters, we can usually get away with 8 bit, or 32 bit operations. The result is that for a typical function (fewer than 64 args, args expected to be by value) the reffiness guard is now test <mask>, Func::m_refBitVec[0] jne exit Rather than: move <mask>, reg1 xor reg2, reg2 cmp 1, Func::m_numParams jnl ok test AttrVarArgs, Func::m_attrs jne exit jmp done ok: load reg3, Func::m_refBitVec[0] and mask, reg3 cmp reg3, reg2 jne exit done:
Esse commit está contido em:
@@ -398,16 +398,15 @@ CheckInitMem S0:PtrToGen S1:ConstInt -> L
|
||||
|
||||
If the value at S0 + S1 (in bytes) has type Uninit, branch to L.
|
||||
|
||||
GuardRefs S0:FuncPtr S1:Int S2:Int S3:Int S4:Int S5:Int S6:Int
|
||||
GuardRefs S0:FuncPtr S1:Int S2:Int S3:Int S4:Int S5:Int
|
||||
|
||||
Perform reffiness guard checks. Operands:
|
||||
|
||||
S0 - function pointer for the frame
|
||||
S1 - num params expected in the func
|
||||
S2 - type Int, but actually a pointer to the func bitvector
|
||||
S3 - first bit to check, must be a multiple of 64
|
||||
S4 - mask to check (RefDeps::Record::m_mask entires)
|
||||
S5 - values to check (RefDeps::Record::m_vals entires)
|
||||
S2 - first bit to check, must be a multiple of 64
|
||||
S3 - mask to check (RefDeps::Record::m_mask entires)
|
||||
S4 - values to check (RefDeps::Record::m_vals entires)
|
||||
|
||||
If any of the checks fail, make a fallback jump. (Jump to a service
|
||||
request that will chain to a retranslation of this tracelet.)
|
||||
|
||||
+39
-39
@@ -185,7 +185,7 @@ Func::Func(Unit& unit, Id id, int line1, int line2,
|
||||
, m_baseCls(nullptr)
|
||||
, m_name(name)
|
||||
, m_namedEntity(nullptr)
|
||||
, m_refBitVec(nullptr)
|
||||
, m_refBitVal(0)
|
||||
, m_cachedOffset(0)
|
||||
, m_maxStackCells(0)
|
||||
, m_numParams(0)
|
||||
@@ -208,7 +208,7 @@ Func::Func(Unit& unit, PreClass* preClass, int line1, int line2, Offset base,
|
||||
, m_baseCls(nullptr)
|
||||
, m_name(name)
|
||||
, m_namedEntity(nullptr)
|
||||
, m_refBitVec(nullptr)
|
||||
, m_refBitVal(0)
|
||||
, m_cachedOffset(0)
|
||||
, m_maxStackCells(0)
|
||||
, m_numParams(0)
|
||||
@@ -394,56 +394,53 @@ bool Func::isNameBindingImmutable(const Unit* fromUnit) const {
|
||||
}
|
||||
|
||||
bool Func::byRef(int32_t arg) const {
|
||||
// Super special case. A handful of builtins are varargs functions where the
|
||||
// (not formally declared) varargs are pass-by-reference. psychedelic-kitten
|
||||
if (arg >= m_numParams && info() &&
|
||||
(info()->attribute & (ClassInfo::RefVariableArguments |
|
||||
ClassInfo::MixedVariableArguments))) {
|
||||
return true;
|
||||
const uint64_t* ref = &m_refBitVal;
|
||||
assert(arg >= 0);
|
||||
if (UNLIKELY(arg >= kBitsPerQword)) {
|
||||
// Super special case. A handful of builtins are varargs functions where the
|
||||
// (not formally declared) varargs are pass-by-reference. psychedelic-kitten
|
||||
if (arg >= m_numParams) {
|
||||
return m_attrs & AttrVariadicByRef;
|
||||
}
|
||||
ref = &shared()->m_refBitPtr[(uint32_t)arg / kBitsPerQword - 1];
|
||||
}
|
||||
int qword = arg / kBitsPerQword;
|
||||
int bit = arg % kBitsPerQword;
|
||||
bool retval = arg < m_numParams && (m_refBitVec[qword] & (1ull << bit)) != 0;
|
||||
return retval;
|
||||
int bit = (uint32_t)arg % kBitsPerQword;
|
||||
return *ref & (1ull << bit);
|
||||
}
|
||||
|
||||
bool Func::mustBeRef(int32_t arg) const {
|
||||
// return true if the argument is required to be a reference
|
||||
// (and thus should be an lvalue)
|
||||
if (arg >= m_numParams && info() &&
|
||||
((info()->attribute & (ClassInfo::RefVariableArguments |
|
||||
ClassInfo::MixedVariableArguments)) ==
|
||||
ClassInfo::RefVariableArguments)) {
|
||||
return true;
|
||||
if (byRef(arg)) {
|
||||
return arg < m_numParams || !(m_attrs & AttrVariadicByRef) ||
|
||||
!info() || !(info()->attribute & ClassInfo::MixedVariableArguments);
|
||||
}
|
||||
int qword = arg / kBitsPerQword;
|
||||
int bit = arg % kBitsPerQword;
|
||||
bool retval = arg < m_numParams && (m_refBitVec[qword] & (1ull << bit)) != 0;
|
||||
return retval;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Func::appendParam(bool ref, const Func::ParamInfo& info,
|
||||
std::vector<ParamInfo>& pBuilder) {
|
||||
int qword = m_numParams / kBitsPerQword;
|
||||
int bit = m_numParams % kBitsPerQword;
|
||||
m_numParams++;
|
||||
uint64_t* refBits = &m_refBitVal;
|
||||
// Grow args, if necessary.
|
||||
if ((m_numParams++ & (kBitsPerQword - 1)) == 0) {
|
||||
assert(shared()->m_refBitVec == m_refBitVec);
|
||||
shared()->m_refBitVec = m_refBitVec = (uint64_t*)
|
||||
realloc(shared()->m_refBitVec,
|
||||
// E.g., 65th m_numParams -> 2 qwords
|
||||
(1 + m_numParams / kBitsPerQword) * sizeof(uint64_t));
|
||||
if (qword) {
|
||||
if (bit == 0) {
|
||||
shared()->m_refBitPtr = (uint64_t*)
|
||||
realloc(shared()->m_refBitPtr, qword * sizeof(uint64_t));
|
||||
}
|
||||
refBits = shared()->m_refBitPtr + qword - 1;
|
||||
}
|
||||
|
||||
if (bit == 0) {
|
||||
// The new word is either zerod or set to 1, depending on whether
|
||||
// we are one of the special builtins that takes variadic
|
||||
// reference arguments. This is for use in the translator.
|
||||
shared()->m_refBitVec[m_numParams / kBitsPerQword] =
|
||||
(m_attrs & AttrVariadicByRef) ? -1ull : 0;
|
||||
*refBits = (m_attrs & AttrVariadicByRef) ? -1ull : 0;
|
||||
}
|
||||
assert(!!(shared()->m_refBitVec[qword] & (uint64_t(1) << bit)) ==
|
||||
!!(m_attrs & AttrVariadicByRef));
|
||||
shared()->m_refBitVec[qword] &= ~(1ull << bit);
|
||||
shared()->m_refBitVec[qword] |= uint64_t(ref) << bit;
|
||||
|
||||
assert(!(*refBits & (uint64_t(1) << bit)) == !(m_attrs & AttrVariadicByRef));
|
||||
*refBits &= ~(1ull << bit);
|
||||
*refBits |= uint64_t(ref) << bit;
|
||||
pBuilder.push_back(info);
|
||||
}
|
||||
|
||||
@@ -642,16 +639,14 @@ Func::SharedData::SharedData(PreClass* preClass, Id id,
|
||||
: m_preClass(preClass), m_id(id), m_base(base),
|
||||
m_numLocals(0), m_numIterators(0),
|
||||
m_past(past), m_line1(line1), m_line2(line2),
|
||||
m_info(nullptr), m_refBitVec(nullptr), m_builtinFuncPtr(nullptr),
|
||||
m_info(nullptr), m_refBitPtr(0), m_builtinFuncPtr(nullptr),
|
||||
m_docComment(docComment), m_top(top), m_isClosureBody(false),
|
||||
m_isGenerator(false), m_isGeneratorFromClosure(false),
|
||||
m_hasGeneratorAsBody(false), m_originalFilename(nullptr) {
|
||||
}
|
||||
|
||||
Func::SharedData::~SharedData() {
|
||||
if (m_refBitVec) {
|
||||
free(m_refBitVec);
|
||||
}
|
||||
free(m_refBitPtr);
|
||||
}
|
||||
|
||||
void Func::SharedData::atomicRelease() {
|
||||
@@ -952,6 +947,11 @@ Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
|
||||
pi.setUserType(m_params[i].userType());
|
||||
f->appendParam(m_params[i].ref(), pi, pBuilder);
|
||||
}
|
||||
if (!m_params.size()) {
|
||||
assert(!f->m_refBitVal && !f->shared()->m_refBitPtr);
|
||||
f->m_refBitVal = attrs & AttrVariadicByRef ? -1uLL : 0uLL;
|
||||
}
|
||||
|
||||
f->shared()->m_params = pBuilder;
|
||||
f->shared()->m_localNames.create(m_localNames);
|
||||
f->shared()->m_numLocals = m_numLocals;
|
||||
|
||||
@@ -431,12 +431,13 @@ public: // Offset accessors for the translator.
|
||||
X(unit);
|
||||
X(cls);
|
||||
X(numParams);
|
||||
X(refBitVec);
|
||||
X(refBitVal);
|
||||
X(fullName);
|
||||
X(prologueTable);
|
||||
X(maybeIntercepted);
|
||||
X(maxStackCells);
|
||||
X(funcBody);
|
||||
X(shared);
|
||||
#undef X
|
||||
|
||||
private:
|
||||
@@ -453,7 +454,9 @@ private:
|
||||
int m_line2;
|
||||
DataType m_returnType;
|
||||
const ClassInfo::MethodInfo* m_info; // For builtins.
|
||||
uint64_t* m_refBitVec;
|
||||
// bits 64 and up of the reffiness guards (first 64 bits
|
||||
// are in Func::m_refBitVal)
|
||||
uint64_t* m_refBitPtr;
|
||||
BuiltinFunction m_builtinFuncPtr;
|
||||
BuiltinFunction m_nativeFuncPtr;
|
||||
ParamInfoVec m_params; // m_params[i] corresponds to parameter i.
|
||||
@@ -508,7 +511,7 @@ private:
|
||||
const NamedEntity* m_namedEntity;
|
||||
Slot m_methodSlot;
|
||||
};
|
||||
uint64_t* m_refBitVec;
|
||||
uint64_t m_refBitVal;
|
||||
public: // used by Unit
|
||||
unsigned m_cachedOffset;
|
||||
private:
|
||||
|
||||
@@ -4221,14 +4221,13 @@ void CodeGenerator::cgCheckTypeMem(IRInstruction* inst) {
|
||||
}
|
||||
|
||||
void CodeGenerator::cgGuardRefs(IRInstruction* inst) {
|
||||
assert(inst->numSrcs() == 6);
|
||||
assert(inst->numSrcs() == 5);
|
||||
|
||||
SSATmp* funcPtrTmp = inst->src(0);
|
||||
SSATmp* nParamsTmp = inst->src(1);
|
||||
SSATmp* bitsPtrTmp = inst->src(2);
|
||||
SSATmp* firstBitNumTmp = inst->src(3);
|
||||
SSATmp* mask64Tmp = inst->src(4);
|
||||
SSATmp* vals64Tmp = inst->src(5);
|
||||
SSATmp* firstBitNumTmp = inst->src(2);
|
||||
SSATmp* mask64Tmp = inst->src(3);
|
||||
SSATmp* vals64Tmp = inst->src(4);
|
||||
|
||||
// Get values in place
|
||||
assert(funcPtrTmp->type() == Type::Func);
|
||||
@@ -4237,63 +4236,106 @@ void CodeGenerator::cgGuardRefs(IRInstruction* inst) {
|
||||
|
||||
assert(nParamsTmp->type() == Type::Int);
|
||||
auto nParamsReg = m_regs[nParamsTmp].reg();
|
||||
assert(nParamsReg != InvalidReg);
|
||||
|
||||
assert(bitsPtrTmp->type() == Type::Int);
|
||||
auto bitsPtrReg = m_regs[bitsPtrTmp].reg();
|
||||
assert(bitsPtrReg != InvalidReg);
|
||||
assert(nParamsReg != InvalidReg || nParamsTmp->isConst());
|
||||
|
||||
assert(firstBitNumTmp->isConst() && firstBitNumTmp->type() == Type::Int);
|
||||
uint32_t firstBitNum = (uint32_t)(firstBitNumTmp->getValInt());
|
||||
|
||||
assert(mask64Tmp->type() == Type::Int);
|
||||
assert(mask64Tmp->inst()->op() == LdConst);
|
||||
assert(mask64Tmp->isConst());
|
||||
auto mask64Reg = m_regs[mask64Tmp].reg();
|
||||
assert(mask64Reg != InvalidReg);
|
||||
int64_t mask64 = mask64Tmp->getValInt();
|
||||
assert(mask64Reg != InvalidReg || mask64Tmp->inst()->op() != LdConst);
|
||||
uint64_t mask64 = mask64Tmp->getValInt();
|
||||
assert(mask64);
|
||||
|
||||
assert(vals64Tmp->type() == Type::Int);
|
||||
assert(vals64Tmp->inst()->op() == LdConst);
|
||||
assert(vals64Tmp->isConst());
|
||||
auto vals64Reg = m_regs[vals64Tmp].reg();
|
||||
assert(vals64Reg != InvalidReg);
|
||||
int64_t vals64 = vals64Tmp->getValInt();
|
||||
assert(vals64Reg != InvalidReg || vals64Tmp->inst()->op() != LdConst);
|
||||
uint64_t vals64 = vals64Tmp->getValInt();
|
||||
assert((vals64 & mask64) == vals64);
|
||||
|
||||
auto const destSK = SrcKey(curFunc(), m_curTrace->bcOff());
|
||||
auto const destSR = m_tx64->getSrcRec(destSK);
|
||||
|
||||
auto thenBody = [&] {
|
||||
auto bitsValReg = m_rScratch;
|
||||
// Load the bit values in bitValReg:
|
||||
// bitsValReg <- [bitsValPtr + (firstBitNum / 64)]
|
||||
m_as.load_reg64_disp_reg64(bitsPtrReg,
|
||||
sizeof(uint64_t) * (firstBitNum / 64),
|
||||
bitsValReg);
|
||||
// bitsValReg <- bitsValReg & mask64
|
||||
m_as.and_reg64_reg64(mask64Reg, bitsValReg);
|
||||
auto bitsOff = sizeof(uint64_t) * (firstBitNum / 64);
|
||||
auto cond = CC_NE;
|
||||
auto bitsPtrReg = m_rScratch;
|
||||
|
||||
// If bitsValReg != vals64Reg, then goto Exit
|
||||
m_as.cmp_reg64_reg64(bitsValReg, vals64Reg);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_NE);
|
||||
if (firstBitNum == 0) {
|
||||
bitsOff = Func::refBitValOff();
|
||||
bitsPtrReg = funcPtrReg;
|
||||
} else {
|
||||
m_as.loadq(funcPtrReg[Func::sharedOff()], bitsPtrReg);
|
||||
bitsOff -= sizeof(uint64_t);
|
||||
}
|
||||
|
||||
if (vals64 == 0 || (mask64 & (mask64 - 1)) == 0) {
|
||||
// If vals64 is zero, or we're testing a single
|
||||
// bit, we can get away with a single test,
|
||||
// rather than mask-and-compare
|
||||
if (mask64Reg != InvalidReg) {
|
||||
m_as.testq (mask64Reg, bitsPtrReg[bitsOff]);
|
||||
} else {
|
||||
if (mask64 < 256) {
|
||||
m_as.testb((int8_t)mask64, bitsPtrReg[bitsOff]);
|
||||
} else {
|
||||
m_as.testl((int32_t)mask64, bitsPtrReg[bitsOff]);
|
||||
}
|
||||
}
|
||||
if (vals64) cond = CC_E;
|
||||
} else {
|
||||
auto bitsValReg = m_rScratch;
|
||||
m_as. loadq (bitsPtrReg[bitsOff], bitsValReg);
|
||||
if (debug) bitsPtrReg = InvalidReg;
|
||||
|
||||
// bitsValReg <- bitsValReg & mask64
|
||||
if (mask64Reg != InvalidReg) {
|
||||
m_as. andq (mask64Reg, bitsValReg);
|
||||
} else if (mask64 < 256) {
|
||||
m_as. andb ((int8_t)mask64, rbyte(bitsValReg));
|
||||
} else {
|
||||
m_as. andl ((int32_t)mask64, r32(bitsValReg));
|
||||
}
|
||||
|
||||
// If bitsValReg != vals64, then goto Exit
|
||||
if (vals64Reg != InvalidReg) {
|
||||
m_as. cmpq (vals64Reg, bitsValReg);
|
||||
} else if (mask64 < 256) {
|
||||
assert(vals64 < 256);
|
||||
m_as. cmpb ((int8_t)vals64, rbyte(bitsValReg));
|
||||
} else {
|
||||
m_as. cmpl ((int32_t)vals64, r32(bitsValReg));
|
||||
}
|
||||
}
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, cond);
|
||||
};
|
||||
|
||||
// If few enough args...
|
||||
m_as.cmp_imm32_reg32(firstBitNum + 1, nParamsReg);
|
||||
if (vals64 == 0 && mask64 == 0) {
|
||||
ifThen(m_as, CC_NL, thenBody);
|
||||
} else if (vals64 != 0 && vals64 != mask64) {
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_L);
|
||||
if (firstBitNum == 0) {
|
||||
assert(nParamsReg == InvalidReg);
|
||||
// This is the first 64 bits. No need to check
|
||||
// nParams.
|
||||
thenBody();
|
||||
} else if (vals64 != 0) {
|
||||
ifThenElse(CC_NL, thenBody, /* else */ [&] {
|
||||
// If not special builtin...
|
||||
m_as.testl(AttrVariadicByRef, funcPtrReg[Func::attrsOff()]);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_Z);
|
||||
});
|
||||
} else {
|
||||
ifThenElse(CC_NL, thenBody, /* else */ [&] {
|
||||
m_as.testl(AttrVariadicByRef, funcPtrReg[Func::attrsOff()]);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_NZ);
|
||||
});
|
||||
assert(nParamsReg != InvalidReg);
|
||||
// Check number of args...
|
||||
m_as. cmpq (firstBitNum, nParamsReg);
|
||||
|
||||
if (vals64 != 0 && vals64 != mask64) {
|
||||
// If we're beyond nParams, then either all params
|
||||
// are refs, or all params are non-refs, so if vals64
|
||||
// isn't 0 and isnt mask64, there's no possibility of
|
||||
// a match
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, CC_LE);
|
||||
thenBody();
|
||||
} else {
|
||||
ifThenElse(CC_NLE, thenBody, /* else */ [&] {
|
||||
// If not special builtin...
|
||||
m_as.testl(AttrVariadicByRef, funcPtrReg[Func::attrsOff()]);
|
||||
m_tx64->emitFallbackCondJmp(m_as, *destSR, vals64 ? CC_Z : CC_NZ);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2452,12 +2452,7 @@ void HhbcTranslator::guardRefs(int64_t entryArDelta,
|
||||
const vector<bool>& vals) {
|
||||
int32_t actRecOff = cellsToBytes(entryArDelta);
|
||||
SSATmp* funcPtr = gen(LdARFuncPtr, m_tb->sp(), cns(actRecOff));
|
||||
SSATmp* nParams = gen(
|
||||
LdRaw, Type::Int, funcPtr, cns(RawMemSlot::FuncNumParams)
|
||||
);
|
||||
SSATmp* bitsPtr = gen(
|
||||
LdRaw, Type::Int, funcPtr, cns(RawMemSlot::FuncRefBitVec)
|
||||
);
|
||||
SSATmp* nParams = nullptr;
|
||||
|
||||
for (unsigned i = 0; i < mask.size(); i += 64) {
|
||||
assert(i < vals.size());
|
||||
@@ -2468,14 +2463,22 @@ void HhbcTranslator::guardRefs(int64_t entryArDelta,
|
||||
}
|
||||
uint64_t vals64 = packBitVec(vals, i);
|
||||
|
||||
if (i == 0) {
|
||||
nParams = cns(64);
|
||||
} else if (i == 64) {
|
||||
nParams = gen(
|
||||
LdRaw, Type::Int, funcPtr, cns(RawMemSlot::FuncNumParams)
|
||||
);
|
||||
}
|
||||
SSATmp* maskTmp = !(mask64>>32) ? cns(mask64) : m_tb->genLdConst(mask64);
|
||||
SSATmp* valsTmp = !(vals64>>32) ? cns(vals64) : m_tb->genLdConst(vals64);
|
||||
gen(
|
||||
GuardRefs,
|
||||
funcPtr,
|
||||
nParams,
|
||||
bitsPtr,
|
||||
cns(i),
|
||||
m_tb->genLdConst(mask64),
|
||||
m_tb->genLdConst(vals64)
|
||||
maskTmp,
|
||||
valsTmp
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,6 @@ O(GuardRefs, ND, S(Func) \
|
||||
S(Int) \
|
||||
S(Int) \
|
||||
S(Int) \
|
||||
S(Int) \
|
||||
S(Int), E) \
|
||||
O(AssertLoc, ND, S(FramePtr), E) \
|
||||
O(OverrideLoc, ND, S(FramePtr), E) \
|
||||
@@ -775,7 +774,7 @@ class RawMemSlot {
|
||||
|
||||
enum Kind {
|
||||
ContLabel, ContDone, ContRunning, ContARPtr,
|
||||
StrLen, FuncNumParams, FuncRefBitVec, ContEntry, MisCtx,
|
||||
StrLen, FuncNumParams, ContEntry, MisCtx,
|
||||
MaxKind
|
||||
};
|
||||
|
||||
@@ -787,7 +786,6 @@ class RawMemSlot {
|
||||
case ContARPtr: return GetContARPtr();
|
||||
case StrLen: return GetStrLen();
|
||||
case FuncNumParams: return GetFuncNumParams();
|
||||
case FuncRefBitVec: return GetFuncRefBitVec();
|
||||
case ContEntry: return GetContEntry();
|
||||
case MisCtx: return GetMisCtx();
|
||||
default: not_reached();
|
||||
@@ -826,11 +824,7 @@ class RawMemSlot {
|
||||
return m;
|
||||
}
|
||||
static RawMemSlot& GetFuncNumParams() {
|
||||
static RawMemSlot m(Func::numParamsOff(), Transl::sz::qword, Type::Int);
|
||||
return m;
|
||||
}
|
||||
static RawMemSlot& GetFuncRefBitVec() {
|
||||
static RawMemSlot m(Func::refBitVecOff(), Transl::sz::qword, Type::Int);
|
||||
static RawMemSlot m(Func::numParamsOff(), Transl::sz::dword, Type::Int);
|
||||
return m;
|
||||
}
|
||||
static RawMemSlot& GetContEntry() {
|
||||
|
||||
@@ -2146,9 +2146,10 @@ TranslatorX64::checkRefs(X64Assembler& a,
|
||||
for (RefDeps::ArMap::const_iterator it = refDeps.m_arMap.begin();
|
||||
it != refDeps.m_arMap.end(); ++it) {
|
||||
// Be careful! The actual Func might have fewer refs than the number
|
||||
// of args we're passing. To forestall this, we're going to have to
|
||||
// keep checking i against the number of params. We consider invocations
|
||||
// with too many arguments to have passed their checks.
|
||||
// of args we're passing. To forestall this, we always prepare at
|
||||
// least 64 bits in the Func, and always fill out the refBitVec
|
||||
// to a multiple of 64 bits
|
||||
|
||||
int entryArDelta = it->first;
|
||||
|
||||
m_hhbcTrans->guardRefs(entryArDelta,
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
function f($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8,
|
||||
$a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16,
|
||||
$a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24,
|
||||
$a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32,
|
||||
$a33, $a34, $a35, $a36, $a37, $a38, $a39, $a40,
|
||||
$a41, $a42, $a43, $a44, $a45, $a46, $a47, $a48,
|
||||
$a49, $a50, $a51, $a52, $a53, $a54, $a55, $a56,
|
||||
$a57, $a58, $a59, $a60, $a61, $a62, $a63, $a64,
|
||||
$a65, $a66, $a67, $a68, $a69, $a70, $a71, $a72,
|
||||
$a73, $a74, $a75, $a76) {
|
||||
for ($i = 0; $i < 77; $i++) {
|
||||
echo ${"a".$i}, "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function g($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8,
|
||||
$a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16,
|
||||
$a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24,
|
||||
$a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32,
|
||||
$a33, $a34, $a35, $a36, $a37, $a38, $a39, $a40,
|
||||
$a41, $a42, $a43, $a44, $a45, $a46, $a47, $a48,
|
||||
$a49, $a50, $a51, $a52, $a53, $a54, $a55, $a56,
|
||||
$a57, $a58, $a59, $a60, $a61, $a62, $a63, $a64,
|
||||
$a65, $a66, $a67, $a68, $a69, $a70, $a71, $a72,
|
||||
$a73, $a74, $a75, $a76, $a77, $a78, $a79, $a80,
|
||||
$a81, $a82, $a83, $a84, $a85, $a86, $a87, $a88,
|
||||
$a89, $a90, $a91, $a92, $a93, $a94, $a95, $a96,
|
||||
$a97, $a98, $a99, $a100, $a101, $a102, $a103, $a104,
|
||||
$a105, $a106, $a107, $a108, $a109, $a110, $a111, $a112,
|
||||
$a113, $a114, $a115, $a116, $a117, $a118, $a119, $a120,
|
||||
$a121, $a122, $a123, $a124, $a125, $a126, $a127, $a128,
|
||||
$a129, $a130, $a131, $a132, $a133, $a134, $a135, $a136,
|
||||
$a137, $a138, $a139, $a140
|
||||
) {
|
||||
for ($i = 0; $i < 141; $i++) {
|
||||
echo ${"a".$i}, "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function gr($a0, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8,
|
||||
$a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16,
|
||||
$a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24,
|
||||
$a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32,
|
||||
$a33, $a34, $a35, $a36, $a37, $a38, $a39, $a40,
|
||||
$a41, $a42, $a43, $a44, $a45, $a46, $a47, $a48,
|
||||
$a49, $a50, $a51, $a52, $a53, $a54, $a55, $a56,
|
||||
$a57, $a58, $a59, $a60, $a61, $a62, $a63, &$a64,
|
||||
&$a65, $a66, $a67, $a68, $a69, $a70, $a71, $a72,
|
||||
$a73, $a74, $a75, $a76, $a77, $a78, $a79, $a80,
|
||||
$a81, $a82, $a83, $a84, $a85, $a86, $a87, $a88,
|
||||
$a89, $a90, $a91, $a92, $a93, $a94, $a95, $a96,
|
||||
$a97, $a98, $a99, $a100, $a101, $a102, $a103, $a104,
|
||||
$a105, $a106, $a107, $a108, $a109, $a110, $a111, $a112,
|
||||
$a113, $a114, $a115, $a116, $a117, $a118, $a119, $a120,
|
||||
$a121, $a122, $a123, $a124, $a125, $a126, $a127, $a128,
|
||||
$a129, $a130, $a131, $a132, $a133, $a134, $a135, $a136,
|
||||
$a137, $a138, $a139, $a140
|
||||
) {
|
||||
for ($i = 0; $i < 141; $i++) {
|
||||
${"a".$i}++;
|
||||
}
|
||||
}
|
||||
|
||||
function test($f, $a) {
|
||||
$f($a[0], $a[1], $a[2], $a[3], $a[4],
|
||||
$a[5], $a[6], $a[7], $a[8], $a[9],
|
||||
$a[10], $a[11], $a[12], $a[13], $a[14],
|
||||
$a[15], $a[16], $a[17], $a[18], $a[19],
|
||||
$a[20], $a[21], $a[22], $a[23], $a[24],
|
||||
$a[25], $a[26], $a[27], $a[28], $a[29],
|
||||
$a[30], $a[31], $a[32], $a[33], $a[34],
|
||||
$a[35], $a[36], $a[37], $a[38], $a[39],
|
||||
$a[40], $a[41], $a[42], $a[43], $a[44],
|
||||
$a[45], $a[46], $a[47], $a[48], $a[49],
|
||||
$a[50], $a[51], $a[52], $a[53], $a[54],
|
||||
$a[55], $a[56], $a[57], $a[58], $a[59],
|
||||
$a[60], $a[61], $a[62], $a[63], $a[64],
|
||||
$a[65], $a[66], $a[67], $a[68], $a[69],
|
||||
$a[70], $a[71], $a[72], $a[73], $a[74],
|
||||
$a[75], $a[76], $a[77], $a[78], $a[79],
|
||||
$a[80], $a[81], $a[82], $a[83], $a[84],
|
||||
$a[85], $a[86], $a[87], $a[88], $a[89],
|
||||
$a[90], $a[91], $a[92], $a[93], $a[94],
|
||||
$a[95], $a[96], $a[97], $a[98], $a[99],
|
||||
$a[100], $a[101], $a[102], $a[103], $a[104],
|
||||
$a[105], $a[106], $a[107], $a[108], $a[109],
|
||||
$a[110], $a[111], $a[112], $a[113], $a[114],
|
||||
$a[115], $a[116], $a[117], $a[118], $a[119],
|
||||
$a[120], $a[121], $a[122], $a[123], $a[124],
|
||||
$a[125], $a[126], $a[127], $a[128], $a[129],
|
||||
$a[130], $a[131], $a[132], $a[133], $a[134],
|
||||
$a[135], $a[136], $a[137], $a[138], $a[139],
|
||||
$a[140], $a[141], $a[142], $a[143], $a[144],
|
||||
$a[145], $a[146], $a[147], $a[148], $a[149]);
|
||||
|
||||
var_dump($a);
|
||||
}
|
||||
|
||||
test("f", range(0, 149));
|
||||
test("g", range(0, 149));
|
||||
test("gr", range(0, 149));
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -2,11 +2,20 @@
|
||||
|
||||
class C1 {
|
||||
function __call($a, $b) {
|
||||
echo '.';
|
||||
}
|
||||
}
|
||||
|
||||
class C2 {
|
||||
function __call($a, $b) {
|
||||
echo '+';
|
||||
}
|
||||
}
|
||||
|
||||
class C3 {
|
||||
function maul($a,$b,&$c) {
|
||||
echo '*';
|
||||
$c++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +30,8 @@ function main() {
|
||||
$o->maul(1, 2, $i, 3, 4, 5, 6, 7);
|
||||
$o->maul(1, 2, $i, 3, 4, 5, 6, 7, 8);
|
||||
// Send subsequent passes to C2::__call
|
||||
$o = new C2();
|
||||
$o = $i == 0 ? new C2() : new C3();
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
......
|
||||
++++++
|
||||
******
|
||||
******
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário