JSTrapStatus ScriptDebugPrologue(JSContext *cx, StackFrame *fp) { JS_ASSERT(fp == cx->fp()); if (fp->isFramePushedByExecute()) { if (JSInterpreterHook hook = cx->debugHooks->executeHook) fp->setHookData(hook(cx, Jsvalify(fp), true, 0, cx->debugHooks->executeHookData)); } else { if (JSInterpreterHook hook = cx->debugHooks->callHook) fp->setHookData(hook(cx, Jsvalify(fp), true, 0, cx->debugHooks->callHookData)); } Value rval; JSTrapStatus status = Debugger::onEnterFrame(cx, &rval); switch (status) { case JSTRAP_CONTINUE: break; case JSTRAP_THROW: cx->setPendingException(rval); break; case JSTRAP_ERROR: cx->clearPendingException(); break; case JSTRAP_RETURN: fp->setReturnValue(rval); break; default: JS_NOT_REACHED("bad Debugger::onEnterFrame JSTrapStatus value"); } return status; }
static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type, const jschar *buf, uint32 len) { JSBool ok = JS_FALSE; switch (type) { case JSON_DATA_STRING: ok = HandleString(cx, jp, buf, len); break; case JSON_DATA_KEYSTRING: jp->objectKey = JS_NewUCStringCopyN(cx, buf, len); ok = JS_TRUE; break; case JSON_DATA_NUMBER: ok = HandleNumber(cx, jp, buf, len); break; case JSON_DATA_KEYWORD: ok = HandleKeyword(cx, jp, buf, len); break; default: JS_NOT_REACHED("Should have a JSON data type"); } js_FinishStringBuffer(jp->buffer); js_InitStringBuffer(jp->buffer); return ok; }
bool ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; // by wangqing, 2013-11-26 masm.movl(ImmTag(JSVAL_TAG_INT32), cmpTempRegister); masm.bne(R0.typeReg(), cmpTempRegister, &failure); masm.nop(); switch (op) { case JSOP_BITNOT: masm.notl(R0.payloadReg()); break; case JSOP_NEG: // Guard against 0 and MIN_INT, both result in a double. /* rewrite testl(reg, imm), avoid use cmpTemp2Register by wangqing, 2013-11-22*/ masm.movl(R0.payloadReg(), cmpTempRegister); masm.andl(Imm32(0x7fffffff), cmpTempRegister); masm.beq(cmpTempRegister, zero, &failure); masm.nop(); masm.negl(R0.payloadReg()); break; default: JS_NOT_REACHED("Unexpected op"); return false; } EmitReturnFromIC(masm); masm.bindBranch(&failure); EmitStubGuardFailure(masm); return true; }
bool ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) { Label failure; masm.branchTestInt32(Assembler::NotEqual, R0, &failure); switch (op) { case JSOP_BITNOT: masm.notl(R0.payloadReg()); break; case JSOP_NEG: // Guard against 0 and MIN_INT, both result in a double. masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure); masm.negl(R0.payloadReg()); break; default: JS_NOT_REACHED("Unexpected op"); return false; } EmitReturnFromIC(masm); masm.bind(&failure); EmitStubGuardFailure(masm); return true; }
static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type) { JSBool ok = JS_FALSE; if (!STRING_BUFFER_OK(jp->buffer)) return JS_FALSE; switch (type) { case JSON_DATA_STRING: ok = HandleString(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)); break; case JSON_DATA_KEYSTRING: js_AppendUCString(jp->objectKey, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)); ok = STRING_BUFFER_OK(jp->objectKey); break; case JSON_DATA_NUMBER: ok = HandleNumber(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)); break; case JSON_DATA_KEYWORD: ok = HandleKeyword(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)); break; default: JS_NOT_REACHED("Should have a JSON data type"); } js_FinishStringBuffer(jp->buffer); js_InitStringBuffer(jp->buffer); return ok; }
// The caller must hold the lock. void drop(T item) { for (T *p = busy.begin(); p != busy.end(); p++) { if (*p == item) { *p = busy.back(); busy.popBack(); return; } } JS_NOT_REACHED("removeBusy"); }
Recompiler::PatchableAddress Recompiler::findPatch(JITScript *jit, void **location) { uint8* codeStart = (uint8 *)jit->code.m_code.executableAddress(); CallSite *callSites_ = jit->callSites(); for (uint32 i = 0; i < jit->nCallSites; i++) { if (callSites_[i].codeOffset + codeStart == *location) { PatchableAddress result; result.location = location; result.callSite = callSites_[i]; return result; } } JS_NOT_REACHED("failed to find call site"); return PatchableAddress(); }
void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena) { switch (thingKind) { case FINALIZE_OBJECT0: case FINALIZE_OBJECT0_BACKGROUND: GetSizeAndThings<JSObject>(thingSize, thingsPerArena); break; case FINALIZE_OBJECT2: case FINALIZE_OBJECT2_BACKGROUND: GetSizeAndThings<JSObject_Slots2>(thingSize, thingsPerArena); break; case FINALIZE_OBJECT4: case FINALIZE_OBJECT4_BACKGROUND: GetSizeAndThings<JSObject_Slots4>(thingSize, thingsPerArena); break; case FINALIZE_OBJECT8: case FINALIZE_OBJECT8_BACKGROUND: GetSizeAndThings<JSObject_Slots8>(thingSize, thingsPerArena); break; case FINALIZE_OBJECT12: case FINALIZE_OBJECT12_BACKGROUND: GetSizeAndThings<JSObject_Slots12>(thingSize, thingsPerArena); break; case FINALIZE_OBJECT16: case FINALIZE_OBJECT16_BACKGROUND: GetSizeAndThings<JSObject_Slots16>(thingSize, thingsPerArena); break; case FINALIZE_EXTERNAL_STRING: case FINALIZE_STRING: GetSizeAndThings<JSString>(thingSize, thingsPerArena); break; case FINALIZE_SHORT_STRING: GetSizeAndThings<JSShortString>(thingSize, thingsPerArena); break; case FINALIZE_FUNCTION: GetSizeAndThings<JSFunction>(thingSize, thingsPerArena); break; #if JS_HAS_XML_SUPPORT case FINALIZE_XML: GetSizeAndThings<JSXML>(thingSize, thingsPerArena); break; #endif default: JS_NOT_REACHED("wrong kind"); } }
static JS_REQUIRES_STACK void AssertDownFrameIsConsistent(JSContext* cx, VMSideExit* anchor, FrameInfo* fi) { JS_ASSERT(anchor->recursive_down); JS_ASSERT(anchor->recursive_down->callerHeight == fi->callerHeight); unsigned downPostSlots = fi->callerHeight; TraceType* typeMap = fi->get_typemap(); CaptureStackTypes(cx, 1, typeMap); const TraceType* m1 = anchor->recursive_down->get_typemap(); for (unsigned i = 0; i < downPostSlots; i++) { if (m1[i] == typeMap[i]) continue; if ((typeMap[i] == TT_INT32 && m1[i] == TT_DOUBLE) || (typeMap[i] == TT_DOUBLE && m1[i] == TT_INT32)) { continue; } JS_NOT_REACHED("invalid RECURSIVE_MISMATCH exit"); } JS_ASSERT(memcmp(anchor->recursive_down, fi, sizeof(FrameInfo)) == 0); }
JS_REQUIRES_STACK LIns* TraceRecorder::slurpSlot(LIns* val_ins, jsval* vp, VMSideExit* exit) { switch (exit->slurpType) { case TT_PSEUDOBOOLEAN: return slurpBoolSlot(val_ins, vp, exit); case TT_INT32: return slurpInt32Slot(val_ins, vp, exit); case TT_DOUBLE: return slurpDoubleSlot(val_ins, vp, exit); case TT_STRING: return slurpStringSlot(val_ins, vp, exit); case TT_NULL: return slurpNullSlot(val_ins, vp, exit); case TT_OBJECT: return slurpObjectSlot(val_ins, vp, exit); case TT_FUNCTION: return slurpFunctionSlot(val_ins, vp, exit); default: JS_NOT_REACHED("invalid type in typemap"); return NULL; } }
JSBool js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len) { uint32 i; if (*jp->statep == JSON_PARSE_STATE_INIT) { PushState(jp, JSON_PARSE_STATE_OBJECT_VALUE); } for (i = 0; i < len; i++) { jschar c = data[i]; switch (*jp->statep) { case JSON_PARSE_STATE_VALUE : if (c == ']') { // empty array if (!PopState(jp)) return JS_FALSE; if (*jp->statep != JSON_PARSE_STATE_ARRAY) return JS_FALSE; // unexpected char if (!CloseArray(cx, jp) || !PopState(jp)) return JS_FALSE; break; } if (c == '}') { // we should only find these in OBJECT_KEY state return JS_FALSE; // unexpected failure } if (c == '"') { *jp->statep = JSON_PARSE_STATE_STRING; break; } if (IsNumChar(c)) { *jp->statep = JSON_PARSE_STATE_NUMBER; js_AppendChar(jp->buffer, c); break; } if (JS7_ISLET(c)) { *jp->statep = JSON_PARSE_STATE_KEYWORD; js_AppendChar(jp->buffer, c); break; } // fall through in case the value is an object or array case JSON_PARSE_STATE_OBJECT_VALUE : if (c == '{') { *jp->statep = JSON_PARSE_STATE_OBJECT; if (!OpenObject(cx, jp) || !PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR)) return JS_FALSE; } else if (c == '[') { *jp->statep = JSON_PARSE_STATE_ARRAY; if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE)) return JS_FALSE; } else if (!JS_ISXMLSPACE(c)) { return JS_FALSE; // unexpected } break; case JSON_PARSE_STATE_OBJECT : if (c == '}') { if (!CloseObject(cx, jp) || !PopState(jp)) return JS_FALSE; } else if (c == ',') { if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR)) return JS_FALSE; } else if (c == ']' || !JS_ISXMLSPACE(c)) { return JS_FALSE; // unexpected } break; case JSON_PARSE_STATE_ARRAY : if (c == ']') { if (!CloseArray(cx, jp) || !PopState(jp)) return JS_FALSE; } else if (c == ',') { if (!PushState(jp, JSON_PARSE_STATE_VALUE)) return JS_FALSE; } else if (!JS_ISXMLSPACE(c)) { return JS_FALSE; // unexpected } break; case JSON_PARSE_STATE_OBJECT_PAIR : if (c == '"') { // we want to be waiting for a : when the string has been read *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR; if (!PushState(jp, JSON_PARSE_STATE_STRING)) return JS_FALSE; } else if (c == '}') { // pop off the object pair state and the object state if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp)) return JS_FALSE; } else if (c == ']' || !JS_ISXMLSPACE(c)) { return JS_FALSE; // unexpected } break; case JSON_PARSE_STATE_OBJECT_IN_PAIR: if (c == ':') { *jp->statep = JSON_PARSE_STATE_VALUE; } else if (!JS_ISXMLSPACE(c)) { return JS_FALSE; // unexpected } break; case JSON_PARSE_STATE_STRING: if (c == '"') { if (!PopState(jp)) return JS_FALSE; JSONDataType jdt; if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) { jdt = JSON_DATA_KEYSTRING; } else { jdt = JSON_DATA_STRING; } if (!HandleData(cx, jp, jdt, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer))) return JS_FALSE; } else if (c == '\\') { *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE; } else { js_AppendChar(jp->buffer, c); } break; case JSON_PARSE_STATE_STRING_ESCAPE: switch(c) { case '"': case '\\': case '/': break; case 'b' : c = '\b'; break; case 'f' : c = '\f'; break; case 'n' : c = '\n'; break; case 'r' : c = '\r'; break; case 't' : c = '\t'; break; default : if (c == 'u') { jp->numHex = 0; jp->hexChar = 0; *jp->statep = JSON_PARSE_STATE_STRING_HEX; continue; } else { return JS_FALSE; // unexpected } } js_AppendChar(jp->buffer, c); *jp->statep = JSON_PARSE_STATE_STRING; break; case JSON_PARSE_STATE_STRING_HEX: if (('0' <= c) && (c <= '9')) jp->hexChar = (jp->hexChar << 4) | (c - '0'); else if (('a' <= c) && (c <= 'f')) jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a); else if (('A' <= c) && (c <= 'F')) jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a); else return JS_FALSE; // unexpected if (++(jp->numHex) == 4) { js_AppendChar(jp->buffer, jp->hexChar); jp->hexChar = 0; jp->numHex = 0; *jp->statep = JSON_PARSE_STATE_STRING; } break; case JSON_PARSE_STATE_KEYWORD: if (JS7_ISLET(c)) { js_AppendChar(jp->buffer, c); } else { // this character isn't part of the keyword, process it again i--; if(!PopState(jp)) return JS_FALSE; if (!HandleData(cx, jp, JSON_DATA_KEYWORD, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer))) return JS_FALSE; } break; case JSON_PARSE_STATE_NUMBER: if (IsNumChar(c)) { js_AppendChar(jp->buffer, c); } else { // this character isn't part of the number, process it again i--; if(!PopState(jp)) return JS_FALSE; if (!HandleData(cx, jp, JSON_DATA_NUMBER, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer))) return JS_FALSE; } break; case JSON_PARSE_STATE_FINISHED: if (!JS_ISXMLSPACE(c)) return JS_FALSE; // extra input break; default: JS_NOT_REACHED("Invalid JSON parser state"); } } return JS_TRUE; }
void GCMarker::dumpConservativeRoots() { if (!conservativeDumpFileName) return; FILE *fp; if (!strcmp(conservativeDumpFileName, "stdout")) { fp = stdout; } else if (!strcmp(conservativeDumpFileName, "stderr")) { fp = stderr; } else if (!(fp = fopen(conservativeDumpFileName, "aw"))) { fprintf(stderr, "Warning: cannot open %s to dump the conservative roots\n", conservativeDumpFileName); return; } conservativeStats.dump(fp); for (void **thingp = conservativeRoots.begin(); thingp != conservativeRoots.end(); ++thingp) { void *thing = thingp; fprintf(fp, " %p: ", thing); switch (GetGCThingTraceKind(thing)) { default: JS_NOT_REACHED("Unknown trace kind"); case JSTRACE_OBJECT: { JSObject *obj = (JSObject *) thing; fprintf(fp, "object %s", obj->getClass()->name); break; } case JSTRACE_SHAPE: { fprintf(fp, "shape"); break; } case JSTRACE_STRING: { JSString *str = (JSString *) thing; if (str->isLinear()) { char buf[50]; PutEscapedString(buf, sizeof buf, &str->asLinear(), '"'); fprintf(fp, "string %s", buf); } else { fprintf(fp, "rope: length %d", (int)str->length()); } break; } # if JS_HAS_XML_SUPPORT case JSTRACE_XML: { JSXML *xml = (JSXML *) thing; fprintf(fp, "xml %u", (unsigned)xml->xml_class); break; } # endif } fputc('\n', fp); } fputc('\n', fp); if (fp != stdout && fp != stderr) fclose(fp); }
// ICBinaryArith_Int32 // by wangqing, 2013-11-22 bool ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) { // Guard that R0 is an integer and R1 is an integer. Label failure; masm.movl(ImmTag(JSVAL_TAG_INT32), cmpTempRegister); masm.bne(R0.typeReg(), cmpTempRegister, &failure); masm.nop(); masm.movl(ImmTag(JSVAL_TAG_INT32), cmpTempRegister); masm.bne(R1.typeReg(), cmpTempRegister, &failure); masm.nop(); // Add R0 and R1. Don't need to explicitly unbox, just use the TailCallReg which // should be available. Register scratchReg = BaselineTailCallReg; Label revertRegister, maybeNegZero; // xsb:fix me // fixed by weizhenwei, 2013.11.05 switch(op_) { case JSOP_ADD: // Add R0 and R1. Don't need to explicitly unbox. // mov tow oprand to cmp registers to prepared for Overflow check. masm.cmpl(R0.payloadReg(), R1.payloadReg()); masm.negl(cmpTemp2Register); // do the add masm.movl(R0.payloadReg(), scratchReg); masm.addl(R1.payloadReg(), scratchReg); // Just jump to failure on overflow. R0 and R1 are preserved, so we can just jump to // the next stub. masm.xorInsn(dataTempRegister, cmpTempRegister, cmpTemp2Register); masm.bgez(dataTempRegister, 7); masm.nop(); masm.subu(dataTempRegister, cmpTempRegister, cmpTemp2Register); masm.xorInsn(dataTempRegister, dataTempRegister, cmpTempRegister); masm.bgez(dataTempRegister, 3); masm.nop(); masm.b(&failure); masm.nop(); // Just overwrite the payload, the tag is still fine. masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_SUB: masm.cmpl(R0.payloadReg(), R1.payloadReg()); masm.movl(R0.payloadReg(), scratchReg); masm.subl(R1.payloadReg(), scratchReg); // jump to failure on overflow, by wangqing, 2013-11-22 masm.xorInsn(dataTempRegister, cmpTempRegister, cmpTemp2Register); masm.bgez(dataTempRegister, 7); masm.nop(); masm.subu(dataTempRegister, cmpTempRegister, cmpTemp2Register); masm.xorInsn(dataTempRegister, dataTempRegister, cmpTempRegister); masm.bgez(dataTempRegister, 3); masm.nop(); masm.b(&failure); masm.nop(); masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_MUL: masm.movl(R0.payloadReg(), scratchReg); masm.imull(R1.payloadReg(), scratchReg); // test whether signed multiply overflow. by weizhenwei, 2013.11.01 masm.mfhi(cmpTempRegister); masm.mflo(cmpTemp2Register); masm.sarl(Imm32(0x1f), cmpTemp2Register); masm.bne(cmpTempRegister, cmpTemp2Register, &failure); masm.nop(); masm.beq(scratchReg, zero, &maybeNegZero); masm.nop(); masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_DIV: // Prevent division by 0. masm.beq(R1.payloadReg(), zero, &failure); masm.nop(); // Prevent negative 0 and -2147483648 / -1. /* rewrite testl(reg, imm), avoid use cmpTemp2Register by wangqing, 2013-11-22*/ masm.movl(R0.payloadReg(), cmpTempRegister); masm.andl(Imm32(0x7fffffff), cmpTempRegister); masm.beq(cmpTempRegister, zero, &failure); masm.nop(); // Preserve R0.payloadReg() masm.div(R0.payloadReg(), R1.payloadReg()); // A remainder implies a double result. // by weizhenwei, 2013.11.02 masm.mfhi(cmpTempRegister); masm.bne(cmpTempRegister, zero, &failure); masm.nop(); // by weizhenwei, 2013.11.05 masm.mflo(R0.payloadReg()); break; case JSOP_MOD: { // x % 0 always results in NaN. masm.beq(R1.payloadReg(), zero, &failure); masm.nop(); // Prevent negative 0 and -2147483648 % -1. /* rewrite testl(reg, imm), avoid use cmpTemp2Register by wangqing, 2013-11-22*/ masm.movl(R0.payloadReg(), cmpTempRegister); masm.andl(Imm32(0x7fffffff), cmpTempRegister); masm.beq(cmpTempRegister, zero, &failure); masm.nop(); masm.div(R0.payloadReg(), R1.payloadReg()); // Fail when we would need a negative remainder. Label done; masm.mfhi(cmpTempRegister); masm.bne(cmpTempRegister, zero, &done); masm.nop(); masm.bltz(R0.payloadReg(), &failure); masm.nop(); masm.bltz(R1.payloadReg(), &failure); masm.nop(); masm.bindBranch(&done); //move reminder to R0.payloadReg, by weizhenwei, 2013.11.05 masm.mfhi(R0.payloadReg()); break; } case JSOP_BITOR: // We can overide R0, because the instruction is unfailable. // The R0.typeReg() is also still intact. masm.orl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_BITXOR: masm.xorl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_BITAND: masm.andl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_LSH: // R0.payloadReg() is result, R1.payloadReg90 is shiftAmount. // rewrite by weizhenwei, 2013.11.06 masm.sllv(R0.payloadReg(), R0.payloadReg(), R1.payloadReg()); // We need to tag again, because we overwrote it. masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); break; case JSOP_RSH: // R0.payloadReg() is result, R1.payloadReg90 is shiftAmount. // rewrite by weizhenwei, 2013.11.06 masm.srav(R0.payloadReg(), R0.payloadReg(), R1.payloadReg()); // We need to tag again, because we overwrote it. masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); break; case JSOP_URSH: if (!allowDouble_) masm.movl(R0.payloadReg(), scratchReg); // R0.payloadReg() is result, R1.payloadReg() is shiftAmount. // rewrite by weizhenwei, 2013.11.06 masm.srlv(R0.payloadReg(), R0.payloadReg(), R1.payloadReg()); // by wangqing. 2013-11-22 if (allowDouble_) { Label toUint; masm.bltz(R0.payloadReg(),&toUint); masm.nop(); // Box and return. masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); EmitReturnFromIC(masm); masm.bindBranch(&toUint); masm.convertUInt32ToDouble(R0.payloadReg(), ScratchFloatReg); masm.boxDouble(ScratchFloatReg, R0); } else { masm.bltz(R0.payloadReg(),&revertRegister); masm.nop(); masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); } break; default: JS_NOT_REACHED("Unhandled op for BinaryArith_Int32. "); return false; } // Return. EmitReturnFromIC(masm); switch(op_) { case JSOP_MUL: masm.bindBranch(&maybeNegZero); // Result is -0 if exactly one of lhs or rhs is negative. masm.movl(R0.payloadReg(), scratchReg); masm.orl(R1.payloadReg(), scratchReg); //add by QuQiuwen; masm.bltz(scratchReg, &failure); masm.nop(); // Result is +0. masm.xorl(R0.payloadReg(), R0.payloadReg()); EmitReturnFromIC(masm); break; case JSOP_URSH: // Revert the content of R0 in the fallible >>> case. if (!allowDouble_) { masm.bindBranch(&revertRegister); masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); } break; default: // No special failure handling required. // Fall through to failure. break; } // Failure case - jump to next stub masm.bindBranch(&failure); EmitStubGuardFailure(masm); return true; }
bool ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler &masm) { // Guard that R0 is an integer and R1 is an integer. Label failure; masm.branchTestInt32(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); // Add R0 and R1. Don't need to explicitly unbox, just use the TailCallReg which // should be available. Register scratchReg = BaselineTailCallReg; Label revertRegister, maybeNegZero; switch(op_) { case JSOP_ADD: // Add R0 and R1. Don't need to explicitly unbox. masm.movl(R0.payloadReg(), scratchReg); masm.addl(R1.payloadReg(), scratchReg); // Just jump to failure on overflow. R0 and R1 are preserved, so we can just jump to // the next stub. masm.j(Assembler::Overflow, &failure); // Just overwrite the payload, the tag is still fine. masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_SUB: masm.movl(R0.payloadReg(), scratchReg); masm.subl(R1.payloadReg(), scratchReg); masm.j(Assembler::Overflow, &failure); masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_MUL: masm.movl(R0.payloadReg(), scratchReg); masm.imull(R1.payloadReg(), scratchReg); masm.j(Assembler::Overflow, &failure); masm.testl(scratchReg, scratchReg); masm.j(Assembler::Zero, &maybeNegZero); masm.movl(scratchReg, R0.payloadReg()); break; case JSOP_DIV: // Prevent division by 0. masm.branchTest32(Assembler::Zero, R1.payloadReg(), R1.payloadReg(), &failure); // Prevent negative 0 and -2147483648 / -1. masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure); // For idiv we need eax. JS_ASSERT(R1.typeReg() == eax); masm.movl(R0.payloadReg(), eax); // Preserve R0.payloadReg()/edx, eax is JSVAL_TYPE_INT32. masm.movl(R0.payloadReg(), scratchReg); // Sign extend eax into edx to make (edx:eax), since idiv is 64-bit. masm.cdq(); masm.idiv(R1.payloadReg()); // A remainder implies a double result. masm.branchTest32(Assembler::NonZero, edx, edx, &revertRegister); masm.movl(eax, R0.payloadReg()); break; case JSOP_MOD: { // x % 0 always results in NaN. masm.branchTest32(Assembler::Zero, R1.payloadReg(), R1.payloadReg(), &failure); // Prevent negative 0 and -2147483648 % -1. masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure); // For idiv we need eax. JS_ASSERT(R1.typeReg() == eax); masm.movl(R0.payloadReg(), eax); // Preserve R0.payloadReg()/edx, eax is JSVAL_TYPE_INT32. masm.movl(R0.payloadReg(), scratchReg); // Sign extend eax into edx to make (edx:eax), since idiv is 64-bit. masm.cdq(); masm.idiv(R1.payloadReg()); // Fail when we would need a negative remainder. Label done; masm.branchTest32(Assembler::NonZero, edx, edx, &done); masm.branchTest32(Assembler::Signed, scratchReg, scratchReg, &revertRegister); masm.branchTest32(Assembler::Signed, R1.payloadReg(), R1.payloadReg(), &revertRegister); masm.bind(&done); // Result is in edx, tag in ecx remains untouched. JS_ASSERT(R0.payloadReg() == edx); JS_ASSERT(R0.typeReg() == ecx); break; } case JSOP_BITOR: // We can overide R0, because the instruction is unfailable. // The R0.typeReg() is also still intact. masm.orl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_BITXOR: masm.xorl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_BITAND: masm.andl(R1.payloadReg(), R0.payloadReg()); break; case JSOP_LSH: // RHS needs to be in ecx for shift operations. JS_ASSERT(R0.typeReg() == ecx); masm.movl(R1.payloadReg(), ecx); masm.shll_cl(R0.payloadReg()); // We need to tag again, because we overwrote it. masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); break; case JSOP_RSH: masm.movl(R1.payloadReg(), ecx); masm.sarl_cl(R0.payloadReg()); masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); break; case JSOP_URSH: if (!allowDouble_) masm.movl(R0.payloadReg(), scratchReg); masm.movl(R1.payloadReg(), ecx); masm.shrl_cl(R0.payloadReg()); masm.testl(R0.payloadReg(), R0.payloadReg()); if (allowDouble_) { Label toUint; masm.j(Assembler::Signed, &toUint); // Box and return. masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); EmitReturnFromIC(masm); masm.bind(&toUint); masm.convertUInt32ToDouble(R0.payloadReg(), ScratchFloatReg); masm.boxDouble(ScratchFloatReg, R0); } else { masm.j(Assembler::Signed, &revertRegister); masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0); } break; default: JS_NOT_REACHED("Unhandled op for BinaryArith_Int32. "); return false; } // Return. EmitReturnFromIC(masm); switch(op_) { case JSOP_MUL: masm.bind(&maybeNegZero); // Result is -0 if exactly one of lhs or rhs is negative. masm.movl(R0.payloadReg(), scratchReg); masm.orl(R1.payloadReg(), scratchReg); masm.j(Assembler::Signed, &failure); // Result is +0. masm.xorl(R0.payloadReg(), R0.payloadReg()); EmitReturnFromIC(masm); break; case JSOP_DIV: case JSOP_MOD: masm.bind(&revertRegister); masm.movl(scratchReg, R0.payloadReg()); masm.movl(ImmType(JSVAL_TYPE_INT32), R1.typeReg()); break; case JSOP_URSH: // Revert the content of R0 in the fallible >>> case. if (!allowDouble_) { masm.bind(&revertRegister); masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); } break; default: // No special failure handling required. // Fall through to failure. break; } // Failure case - jump to next stub masm.bind(&failure); EmitStubGuardFailure(masm); return true; }
JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::slurpDownFrames(jsbytecode* return_pc) { /* Missing - no go */ if (cx->fp->argc != cx->fp->fun->nargs) RETURN_STOP_A("argc != nargs"); LIns* argv_ins; unsigned frameDepth; unsigned downPostSlots; JSStackFrame* fp = cx->fp; LIns* fp_ins = addName(lir->insLoad(LIR_ldp, cx_ins, offsetof(JSContext, fp)), "fp"); /* * When first emitting slurp code, do so against the down frame. After * popping the interpreter frame, it is illegal to resume here, as the * down frame has been moved up. So all this code should be skipped if * anchoring off such an exit. */ if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { fp_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, down)), "downFp"); fp = fp->down; argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv)), "argv"); /* If recovering from a SLURP_MISMATCH, all of this is unnecessary. */ if (!anchor || anchor->exitType != RECURSIVE_SLURP_MISMATCH_EXIT) { /* fp->down should not be NULL. */ guard(false, lir->ins_peq0(fp_ins), RECURSIVE_LOOP_EXIT); /* fp->down->argv should not be NULL. */ guard(false, lir->ins_peq0(argv_ins), RECURSIVE_LOOP_EXIT); /* * Guard on the script being the same. This might seem unnecessary, * but it lets the recursive loop end cleanly if it doesn't match. * With only the pc check, it is harder to differentiate between * end-of-recursion and recursion-returns-to-different-pc. */ guard(true, lir->ins2(LIR_peq, addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, script)), "script"), INS_CONSTPTR(cx->fp->down->script)), RECURSIVE_LOOP_EXIT); } /* fp->down->regs->pc should be == pc. */ guard(true, lir->ins2(LIR_peq, lir->insLoad(LIR_ldp, addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, regs)), "regs"), offsetof(JSFrameRegs, pc)), INS_CONSTPTR(return_pc)), RECURSIVE_SLURP_MISMATCH_EXIT); /* fp->down->argc should be == argc. */ guard(true, lir->ins2(LIR_eq, addName(lir->insLoad(LIR_ld, fp_ins, offsetof(JSStackFrame, argc)), "argc"), INS_CONST(cx->fp->argc)), MISMATCH_EXIT); /* Pop the interpreter frame. */ LIns* args[] = { lirbuf->state, cx_ins }; guard(false, lir->ins_eq0(lir->insCall(&js_PopInterpFrame_ci, args)), MISMATCH_EXIT); /* Compute slots for the down frame. */ downPostSlots = NativeStackSlots(cx, 1) - NativeStackSlots(cx, 0); frameDepth = 1; } else { /* Note: loading argv from fp, not fp->down. */ argv_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argv)), "argv"); /* Slots for this frame, minus the return value. */ downPostSlots = NativeStackSlots(cx, 0) - 1; frameDepth = 0; } /* * This is a special exit used as a template for the stack-slurping code. * LeaveTree will ignore all but the final slot, which contains the return * value. The slurpSlot variable keeps track of the last slot that has been * unboxed, as to avoid re-unboxing when taking a SLURP_FAIL exit. */ unsigned numGlobalSlots = tree->globalSlots->length(); unsigned safeSlots = NativeStackSlots(cx, frameDepth) + 1 + numGlobalSlots; jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH; VMSideExit* exit = (VMSideExit*) traceMonitor->traceAlloc->alloc(sizeof(VMSideExit) + sizeof(TraceType) * safeSlots); memset(exit, 0, sizeof(VMSideExit)); exit->pc = (jsbytecode*)recursive_pc; exit->from = fragment; exit->exitType = RECURSIVE_SLURP_FAIL_EXIT; exit->numStackSlots = downPostSlots + 1; exit->numGlobalSlots = numGlobalSlots; exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - tree->nativeStackBase; exit->recursive_pc = recursive_pc; /* * Build the exit typemap. This may capture extra types, but they are * thrown away. */ TraceType* typeMap = exit->stackTypeMap(); jsbytecode* oldpc = cx->fp->regs->pc; cx->fp->regs->pc = exit->pc; CaptureStackTypes(cx, frameDepth, typeMap); cx->fp->regs->pc = oldpc; if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) typeMap[downPostSlots] = determineSlotType(&stackval(-1)); else typeMap[downPostSlots] = anchor->stackTypeMap()[anchor->numStackSlots - 1]; determineGlobalTypes(&typeMap[exit->numStackSlots]); #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); #endif /* * Return values are tricky because there are two cases. Anchoring off a * slurp failure (the second case) means the return value has already been * moved. However it can still be promoted to link trees together, so we * load it from the new location. * * In all other cases, the return value lives in the tracker and it can be * grabbed safely. */ LIns* rval_ins; TraceType returnType = exit->stackTypeMap()[downPostSlots]; if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { rval_ins = get(&stackval(-1)); if (returnType == TT_INT32) { JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(isPromoteInt(rval_ins)); rval_ins = demote(lir, rval_ins); } /* * The return value must be written out early, before slurping can fail, * otherwise it will not be available when there's a type mismatch. */ lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); } else { switch (returnType) { case TT_PSEUDOBOOLEAN: case TT_INT32: rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); break; case TT_DOUBLE: rval_ins = lir->insLoad(LIR_ldf, lirbuf->sp, exit->sp_adj - sizeof(double)); break; case TT_FUNCTION: case TT_OBJECT: case TT_STRING: case TT_NULL: rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); break; default: JS_NOT_REACHED("unknown type"); RETURN_STOP_A("unknown type"); } } /* Slurp */ SlurpInfo info; info.curSlot = 0; info.exit = exit; info.typeMap = typeMap; info.slurpFailSlot = (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) ? anchor->slurpFailSlot : 0; /* callee */ slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -2 * ptrdiff_t(sizeof(jsval))), &fp->argv[-2], &info); /* this */ slurpSlot(lir->insLoad(LIR_ldp, argv_ins, -1 * ptrdiff_t(sizeof(jsval))), &fp->argv[-1], &info); /* args[0..n] */ for (unsigned i = 0; i < JS_MAX(fp->argc, fp->fun->nargs); i++) slurpSlot(lir->insLoad(LIR_ldp, argv_ins, i * sizeof(jsval)), &fp->argv[i], &info); /* argsobj */ slurpSlot(addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, argsobj)), "argsobj"), &fp->argsobj, &info); /* scopeChain */ slurpSlot(addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, scopeChain)), "scopeChain"), (jsval*) &fp->scopeChain, &info); /* vars */ LIns* slots_ins = addName(lir->insLoad(LIR_ldp, fp_ins, offsetof(JSStackFrame, slots)), "slots"); for (unsigned i = 0; i < fp->script->nfixed; i++) slurpSlot(lir->insLoad(LIR_ldp, slots_ins, i * sizeof(jsval)), &fp->slots[i], &info); /* stack vals */ unsigned nfixed = fp->script->nfixed; jsval* stack = StackBase(fp); LIns* stack_ins = addName(lir->ins2(LIR_piadd, slots_ins, INS_CONSTWORD(nfixed * sizeof(jsval))), "stackBase"); size_t limit = size_t(fp->regs->sp - StackBase(fp)); if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) limit--; else limit -= fp->fun->nargs + 2; for (size_t i = 0; i < limit; i++) slurpSlot(lir->insLoad(LIR_ldp, stack_ins, i * sizeof(jsval)), &stack[i], &info); JS_ASSERT(info.curSlot == downPostSlots); /* Jump back to the start */ exit = copy(exit); exit->exitType = UNSTABLE_LOOP_EXIT; #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); #endif RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(typeMap[i]); slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); debug_only_print0(LC_TMTracer, "Compiling up-recursive slurp...\n"); exit = copy(exit); if (exit->recursive_pc == fragment->root->ip) exit->exitType = UNSTABLE_LOOP_EXIT; else exit->exitType = RECURSIVE_UNLINKED_EXIT; debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit, getExitName(exit->exitType)); JS_ASSERT(tree->recursion >= Recursion_Unwinds); return closeLoop(slotMap, exit); }