bool TypeInferenceOracle::analyzeTypesForInlinableCallees(JSContext *cx, JSScript *script, Vector<JSScript*> &seen) { // Don't analyze scripts which will not be inlined (but always analyze the first script). if (seen.length() > 0 && script->getUseCount() < js_IonOptions.usesBeforeInlining()) return true; for (size_t i = 0; i < seen.length(); i++) { if (seen[i] == script) return true; } if (!seen.append(script)) return false; if (!script->ensureRanInference(cx)) return false; ScriptAnalysis *analysis = script->analysis(); JS_ASSERT(analysis->ranInference()); for (jsbytecode *pc = script->code; pc < script->code + script->length; pc += GetBytecodeLength(pc)) { if (!(js_CodeSpec[*pc].format & JOF_INVOKE)) continue; if (!analysis->maybeCode(pc)) continue; uint32_t argc = GET_ARGC(pc); StackTypeSet *calleeTypes = analysis->poppedTypes(pc, argc + 1); if (!analyzeTypesForInlinableCallees(cx, calleeTypes, seen)) return false; // For foo.call() and foo.apply(), also look for any callees in the // 'this' types of the call, which might be inlined by Ion. if (*pc == JSOP_FUNCALL || *pc == JSOP_FUNAPPLY) { StackTypeSet *thisTypes = analysis->poppedTypes(pc, argc); if (!analyzeTypesForInlinableCallees(cx, thisTypes, seen)) return false; } } return true; }
bool TypeInferenceOracle::getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes) { JS_ASSERT(JSOp(*osrPc) == JSOP_LOOPENTRY); JS_ASSERT(script()->code < osrPc); JS_ASSERT(osrPc < script()->code + script()->length); Vector<types::StackTypeSet *> slotTypeSets(cx); if (!slotTypeSets.resize(TotalSlots(script()))) return false; for (uint32_t slot = ThisSlot(); slot < TotalSlots(script()); slot++) slotTypeSets[slot] = TypeScript::SlotTypes(script(), slot); jsbytecode *pc = script()->code; ScriptAnalysis *analysis = script()->analysis(); // To determine the slot types at the OSR pc, we have to do a forward walk // over the bytecode to reconstruct the types. for (;;) { Bytecode *opinfo = analysis->maybeCode(pc); if (opinfo) { if (opinfo->jumpTarget) { // Update variable types for all new values at this bytecode. if (const SlotValue *newv = analysis->newValues(pc)) { while (newv->slot) { if (newv->slot < TotalSlots(script())) slotTypeSets[newv->slot] = analysis->getValueTypes(newv->value); newv++; } } } if (BytecodeUpdatesSlot(JSOp(*pc))) { uint32_t slot = GetBytecodeSlot(script(), pc); if (analysis->trackSlot(slot)) slotTypeSets[slot] = analysis->pushedTypes(pc, 0); } } if (pc == osrPc) break; pc += GetBytecodeLength(pc); } JS_ASSERT(pc == osrPc); // TI always includes the |this| slot, but Ion only does so for function // scripts. This means we have to subtract 1 for global/eval scripts. JS_ASSERT(ThisSlot() == 1); JS_ASSERT(ArgSlot(0) == 2); #ifdef DEBUG uint32_t stackDepth = analysis->getCode(osrPc).stackDepth; #endif if (script()->function()) { JS_ASSERT(slotTypes.length() == TotalSlots(script()) + stackDepth); for (size_t i = ThisSlot(); i < TotalSlots(script()); i++) slotTypes[i] = getMIRType(slotTypeSets[i]); } else { JS_ASSERT(slotTypes.length() == TotalSlots(script()) + stackDepth - 1); for (size_t i = ArgSlot(0); i < TotalSlots(script()); i++) slotTypes[i - 1] = getMIRType(slotTypeSets[i]); } return true; }