From cbe62cd2cbb24e03e69929d63b079331885406e9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 13 Oct 2010 16:39:20 -0700 Subject: [PATCH] Bug 600779 - TM: allow for branches that are always taken. r=dmandelin. --- js/src/jstracer.cpp | 234 ++++++++++++++++++++++++++++---------------- js/src/jstracer.h | 7 +- 2 files changed, 154 insertions(+), 87 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ea0eaebaf241..9e971649c27b 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2512,10 +2512,10 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag LIns* counterPtr = INS_CONSTPTR((void *) &JS_THREAD_DATA(cx)->iterationCounter); LIns* counterValue = lir->insLoad(LIR_ldi, counterPtr, 0, ACCSET_OTHER, LOAD_VOLATILE); LIns* test = lir->ins2ImmI(LIR_lti, counterValue, MIN_LOOP_ITERS); - LIns *branch = lir->insBranch(LIR_jf, test, NULL); + LIns *branch = unoptimizableCondBranch(LIR_jf, test); counterValue = lir->ins2(LIR_addi, counterValue, INS_CONST(1)); lir->insStore(counterValue, counterPtr, 0, ACCSET_OTHER); - label(branch); + labelForBranch(branch); } #endif } @@ -3964,8 +3964,7 @@ TraceRecorder::addr(Value* p) { return isGlobal(p) ? lir->ins2(LIR_addp, eos_ins, INS_CONSTWORD(nativeGlobalOffset(p))) - : lir->ins2(LIR_addp, lirbuf->sp, - INS_CONSTWORD(nativespOffset(p))); + : lir->ins2(LIR_addp, lirbuf->sp, INS_CONSTWORD(nativespOffset(p))); } JS_REQUIRES_STACK inline bool @@ -5374,13 +5373,13 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) #endif LIns* rec = lir->insCall(ci, args); LIns* lr = lir->insLoad(LIR_ldp, rec, offsetof(GuardRecord, exit), ACCSET_OTHER); - LIns* nested = lir->insBranch(LIR_jt, - lir->ins2ImmI(LIR_eqi, - lir->insLoad(LIR_ldi, lr, - offsetof(VMSideExit, exitType), - ACCSET_OTHER), - NESTED_EXIT), - NULL); + LIns* nested = + unoptimizableCondBranch(LIR_jt, + lir->ins2ImmI(LIR_eqi, + lir->insLoad(LIR_ldi, lr, + offsetof(VMSideExit, exitType), + ACCSET_OTHER), + NESTED_EXIT)); /* * If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard @@ -5395,13 +5394,13 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) * and we unwind the tree call stack. We store the first (innermost) tree call guard in state * and we will try to grow the outer tree the failing call was in starting at that guard. */ - label(nested); - LIns* done2 = lir->insBranch(LIR_jf, - lir->insEqP_0(lir->insLoad(LIR_ldp, - lirbuf->state, - offsetof(TracerState, lastTreeCallGuard), - ACCSET_OTHER)), - NULL); + labelForBranch(nested); + LIns* done2 = + unoptimizableCondBranch(LIR_jf, + lir->insEqP_0(lir->insLoad(LIR_ldp, + lirbuf->state, + offsetof(TracerState, lastTreeCallGuard), + ACCSET_OTHER))); lir->insStore(lr, lirbuf->state, offsetof(TracerState, lastTreeCallGuard), ACCSET_OTHER); lir->insStore(lir->ins2(LIR_addp, lir->insLoad(LIR_ldp, lirbuf->state, offsetof(TracerState, rp), @@ -5413,7 +5412,7 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit) sizeof(void*) == 4 ? 2 : 3))), lirbuf->state, offsetof(TracerState, rpAtLastTreeCall), ACCSET_OTHER); - label(done1, done2); + labelForBranches(done1, done2); /* * Keep updating outermostTreeExit so that TracerState always contains the most recent @@ -8406,12 +8405,14 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) * into -2147483648 / -1, because it can raise an overflow exception. */ if (!d1->isImmI()) { - LIns* gt = lir->insBranch(LIR_jt, lir->ins2ImmI(LIR_gti, d1, 0), NULL); - guard(false, lir->insEqI_0(d1), exit); - guard(false, lir->ins2(LIR_andi, - lir->ins2ImmI(LIR_eqi, d0, 0x80000000), - lir->ins2ImmI(LIR_eqi, d1, -1)), exit); - label(gt); + LIns* br; + if (condBranch(LIR_jt, lir->ins2ImmI(LIR_gti, d1, 0), &br)) { + guard(false, lir->insEqI_0(d1), exit); + guard(false, lir->ins2(LIR_andi, + lir->ins2ImmI(LIR_eqi, d0, 0x80000000), + lir->ins2ImmI(LIR_eqi, d1, -1)), exit); + labelForBranch(br); + } } else { if (d1->immI() == -1) guard(false, lir->ins2ImmI(LIR_eqi, d0, 0x80000000), exit); @@ -8437,14 +8438,15 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) result = lir->ins1(v = LIR_modi, lir->ins2(LIR_divi, d0, d1)); /* If the result is not 0, it is always within the integer domain. */ - LIns* branch = lir->insBranch(LIR_jf, lir->insEqI_0(result), NULL); - - /* - * If the result is zero, we must exit if the lhs is negative since - * the result is -0 in this case, which is not in the integer domain. - */ - guard(false, lir->ins2ImmI(LIR_lti, d0, 0), exit); - label(branch); + LIns* br; + if (condBranch(LIR_jf, lir->insEqI_0(result), &br)) { + /* + * If the result is zero, we must exit if the lhs is negative since + * the result is -0 in this case, which is not in the integer domain. + */ + guard(false, lir->ins2ImmI(LIR_lti, d0, 0), exit); + labelForBranch(br); + } break; } #endif @@ -8482,24 +8484,68 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) return lir->ins1(LIR_i2d, result); } -/* Inserts a label and updates 'branch' to branch to it, if 'branch' is non-NULL. */ -void -TraceRecorder::label(LIns* br) +/* + * If the branch is always taken, return false; the code jumped over by the + * branch need not be generated. If the branch is never taken, return true + * and put NULL in *brOut. Otherwise, return true and put the branch in + * *brOut. + */ +bool +TraceRecorder::condBranch(LOpcode op, LIns* cond, LIns** brOut) { - if (br) - br->setTarget(lir->ins0(LIR_label)); + JS_ASSERT(op == LIR_jt || op == LIR_jf); + *brOut = NULL; + if (cond->isImmI()) { + int32 condval = cond->immI(); + JS_ASSERT(condval == 0 || condval == 1); + if ((op == LIR_jt && condval == 1) || (op == LIR_jf && condval == 0)) + return false; /* branch is always taken */ + } + *brOut = lir->insBranch(op, cond, NULL); + return true; } -/* Similar to the other label(), but for two branches. */ +/* + * Like condBranch(), but for when we know the branch condition cannot be + * optimized to a constant, eg. because one of the operands is the result of a + * volatile load. + */ +LIns* +TraceRecorder::unoptimizableCondBranch(LOpcode op, LIns* cond) +{ + JS_ASSERT(op == LIR_jt || op == LIR_jf); + JS_ASSERT(!cond->isImmI()); + return lir->insBranch(op, cond, NULL); +} + +/* + * Inserts a label and updates 'branch' to branch to it, if 'branch' is non-NULL. + * ('branch' may be NULL if it was a conditional branch and its condition was + * a constant value that resulted in the branch never being taken.) + */ void -TraceRecorder::label(LIns* br1, LIns* br2) +TraceRecorder::labelForBranch(LIns* br) +{ + if (br) { + JS_ASSERT(br->isop(LIR_j) || br->isop(LIR_jt) || br->isop(LIR_jf)); + br->setTarget(lir->ins0(LIR_label)); + } +} + +/* Similar to the other labelForBranch(), but for two branches. */ +void +TraceRecorder::labelForBranches(LIns* br1, LIns* br2) { if (br1 || br2) { LIns* label = lir->ins0(LIR_label); - if (br1) + if (br1) { + JS_ASSERT(br1->isop(LIR_j) || br1->isop(LIR_jt) || br1->isop(LIR_jf)); br1->setTarget(label); - if (br2) + } + if (br2) { + JS_ASSERT(br2->isop(LIR_j) || br2->isop(LIR_jt) || br2->isop(LIR_jf)); br2->setTarget(label); + } } } @@ -10587,17 +10633,22 @@ TraceRecorder::record_JSOP_ARGUMENTS() LIns* mem_ins = lir->insAlloc(sizeof(JSObject *)); - LIns* br1 = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL); - lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); - LIns* br2 = lir->insBranch(LIR_j, NULL, NULL); - - label(br1); - - LIns* call_ins = newArguments(callee_ins, strict); - lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); - - label(br2); + LIns* isZero_ins = lir->insEqP_0(a_ins); + if (isZero_ins->isImmI(0)) { + lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); + } else if (isZero_ins->isImmI(1)) { + LIns* call_ins = newArguments(callee_ins, strict); + lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); + } else { + LIns* br1 = unoptimizableCondBranch(LIR_jt, isZero_ins); + lir->insStore(a_ins, mem_ins, 0, ACCSET_OTHER); + LIns* br2 = lir->insBranch(LIR_j, NULL, NULL); + labelForBranch(br1); + LIns* call_ins = newArguments(callee_ins, strict); + lir->insStore(call_ins, mem_ins, 0, ACCSET_OTHER); + labelForBranch(br2); + } args_ins = lir->insLoad(LIR_ldp, mem_ins, 0, ACCSET_OTHER); } @@ -12530,13 +12581,16 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** idx_ins = lir->insUI2P(idx_ins); LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); - LIns *br = lir->insBranch(LIR_jt, - lir->insEqP_0(lir->ins2(LIR_andp, - length_ins, - INS_CONSTWORD(JSString::ROPE_BIT))), - NULL); - lir->insCall(&js_Flatten_ci, &str_ins); - label(br); + LIns *br; + if (condBranch(LIR_jt, + lir->insEqP_0(lir->ins2(LIR_andp, + length_ins, + INS_CONSTWORD(JSString::ROPE_BIT))), + &br)) + { + lir->insCall(&js_Flatten_ci, &str_ins); + labelForBranch(br); + } guard(true, lir->ins2(LIR_ltup, idx_ins, lir->ins2ImmI(LIR_rshup, length_ins, JSString::FLAGS_LENGTH_SHIFT)), @@ -12576,13 +12630,16 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns *length_ins = lir->insLoad(LIR_ldp, str_ins, offsetof(JSString, mLengthAndFlags), ACCSET_OTHER); - LIns *br1 = lir->insBranch(LIR_jt, - lir->insEqP_0(lir->ins2(LIR_andp, - length_ins, - INS_CONSTWORD(JSString::ROPE_BIT))), - NULL); - lir->insCall(&js_Flatten_ci, &str_ins); - label(br1); + LIns *br1; + if (condBranch(LIR_jt, + lir->insEqP_0(lir->ins2(LIR_andp, + length_ins, + INS_CONSTWORD(JSString::ROPE_BIT))), + &br1)) + { + lir->insCall(&js_Flatten_ci, &str_ins); + labelForBranch(br1); + } LIns* inRange = lir->ins2(LIR_ltup, idx_ins, @@ -12596,11 +12653,12 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns *phi_ins = lir->insAlloc(sizeof(JSString *)); lir->insStore(LIR_stp, INS_CONSTSTR(cx->runtime->emptyString), phi_ins, 0, ACCSET_OTHER); - LIns* br2 = lir->insBranch(LIR_jf, inRange, NULL); - LIns *unitstr_ins = getUnitString(str_ins, idx_ins); - lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); - label(br2); - + LIns* br2; + if (condBranch(LIR_jf, inRange, &br2)) { + LIns *unitstr_ins = getUnitString(str_ins, idx_ins); + lir->insStore(LIR_stp, unitstr_ins, phi_ins, 0, ACCSET_OTHER); + labelForBranch(br2); + } *out = lir->insLoad(LIR_ldp, phi_ins, 0, ACCSET_OTHER); } return RECORD_CONTINUE; @@ -13069,11 +13127,13 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) addName(lir->insLoad(LIR_ldi, obj_ins, offsetof(JSObject, capacity), ACCSET_OTHER), "capacity"); - LIns* br = lir->insBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), NULL); - LIns* args[] = { idx_ins, obj_ins, cx_ins }; - LIns* res_ins = lir->insCall(&js_EnsureDenseArrayCapacity_ci, args); - guard(false, lir->insEqI_0(res_ins), mismatchExit); - label(br); + LIns* br; + if (condBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), &br)) { + LIns* args[] = { idx_ins, obj_ins, cx_ins }; + LIns* res_ins = lir->insCall(&js_EnsureDenseArrayCapacity_ci, args); + guard(false, lir->insEqI_0(res_ins), mismatchExit); + labelForBranch(br); + } // Get the address of the element. LIns *dslots_ins = @@ -13096,12 +13156,14 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) JSVAL_TAG_SHIFT)), #endif INS_CONSTU(JSVAL_TAG_MAGIC)); - LIns* br2 = lir->insBranch(LIR_jf, cond, NULL); - LIns* args2[] = { idx_ins, obj_ins, cx_ins }; - LIns* res_ins2 = addName(lir->insCall(&js_Array_dense_setelem_hole_ci, args2), - "hasNoIndexedProperties"); - guard(false, lir->insEqI_0(res_ins2), mismatchExit); - label(br2); + LIns* br2; + if (condBranch(LIR_jf, cond, &br2)) { + LIns* args[] = { idx_ins, obj_ins, cx_ins }; + LIns* res_ins = addName(lir->insCall(&js_Array_dense_setelem_hole_ci, args), + "hasNoIndexedProperties"); + guard(false, lir->insEqI_0(res_ins), mismatchExit); + labelForBranch(br2); + } // Right, actually set the element. box_value_into(v, v_ins, addr_ins, 0, ACCSET_OTHER); @@ -15347,9 +15409,11 @@ TraceRecorder::record_JSOP_ARGCNT() RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); LIns *a_ins = getFrameObjPtr(fp->addressOfArgs()); if (callDepth == 0) { - LIns *br = lir->insBranch(LIR_jt, lir->insEqP_0(a_ins), NULL); - guardArgsLengthNotAssigned(a_ins); - label(br); + LIns *br; + if (condBranch(LIR_jt, lir->insEqP_0(a_ins), &br)) { + guardArgsLengthNotAssigned(a_ins); + labelForBranch(br); + } } stack(0, lir->insImmD(fp->numActualArgs())); return ARECORD_CONTINUE; diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 3437a387df2f..6370eb8d20cc 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1115,8 +1115,11 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1, nanojit::LIns* s0, nanojit::LIns* s1); - void label(nanojit::LIns* br); - void label(nanojit::LIns* br1, nanojit::LIns* br2); + bool condBranch(nanojit::LOpcode op, nanojit::LIns* cond, nanojit::LIns** brOut); + nanojit::LIns* unoptimizableCondBranch(nanojit::LOpcode op, nanojit::LIns* cond); + void labelForBranch(nanojit::LIns* br); + void labelForBranches(nanojit::LIns* br1, nanojit::LIns* br2); + nanojit::LIns* i2d(nanojit::LIns* i); nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false); nanojit::LIns* f2u(nanojit::LIns* f);