1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/shared/CodeGenerator-shared.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1155 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/shared/CodeGenerator-shared-inl.h" 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 + 1.14 +#include "jit/IonCaches.h" 1.15 +#include "jit/IonMacroAssembler.h" 1.16 +#include "jit/IonSpewer.h" 1.17 +#include "jit/MIR.h" 1.18 +#include "jit/MIRGenerator.h" 1.19 +#include "jit/ParallelFunctions.h" 1.20 +#include "vm/TraceLogging.h" 1.21 + 1.22 +#include "jit/IonFrames-inl.h" 1.23 + 1.24 +using namespace js; 1.25 +using namespace js::jit; 1.26 + 1.27 +using mozilla::DebugOnly; 1.28 + 1.29 +namespace js { 1.30 +namespace jit { 1.31 + 1.32 +MacroAssembler & 1.33 +CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg) 1.34 +{ 1.35 + if (masmArg) 1.36 + return *masmArg; 1.37 + maybeMasm_.construct(); 1.38 + return maybeMasm_.ref(); 1.39 +} 1.40 + 1.41 +CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg) 1.42 + : oolIns(nullptr), 1.43 + maybeMasm_(), 1.44 + masm(ensureMasm(masmArg)), 1.45 + gen(gen), 1.46 + graph(*graph), 1.47 + current(nullptr), 1.48 + snapshots_(), 1.49 + recovers_(), 1.50 + deoptTable_(nullptr), 1.51 +#ifdef DEBUG 1.52 + pushedArgs_(0), 1.53 +#endif 1.54 + lastOsiPointOffset_(0), 1.55 + sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_), 1.56 + osrEntryOffset_(0), 1.57 + skipArgCheckEntryOffset_(0), 1.58 + frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()) 1.59 +{ 1.60 + if (!gen->compilingAsmJS()) 1.61 + masm.setInstrumentation(&sps_); 1.62 + 1.63 + // Since asm.js uses the system ABI which does not necessarily use a 1.64 + // regular array where all slots are sizeof(Value), it maintains the max 1.65 + // argument stack depth separately. 1.66 + if (gen->compilingAsmJS()) { 1.67 + JS_ASSERT(graph->argumentSlotCount() == 0); 1.68 + frameDepth_ += gen->maxAsmJSStackArgBytes(); 1.69 + 1.70 + // An MAsmJSCall does not align the stack pointer at calls sites but instead 1.71 + // relies on the a priori stack adjustment (in the prologue) on platforms 1.72 + // (like x64) which require the stack to be aligned. 1.73 +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) 1.74 + bool forceAlign = true; 1.75 +#else 1.76 + bool forceAlign = false; 1.77 +#endif 1.78 + if (gen->performsAsmJSCall() || forceAlign) { 1.79 + unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_; 1.80 + if (unsigned rem = alignmentAtCall % StackAlignment) 1.81 + frameDepth_ += StackAlignment - rem; 1.82 + } 1.83 + 1.84 + // FrameSizeClass is only used for bailing, which cannot happen in 1.85 + // asm.js code. 1.86 + frameClass_ = FrameSizeClass::None(); 1.87 + } else { 1.88 + frameClass_ = FrameSizeClass::FromDepth(frameDepth_); 1.89 + } 1.90 +} 1.91 + 1.92 +bool 1.93 +CodeGeneratorShared::generateOutOfLineCode() 1.94 +{ 1.95 + for (size_t i = 0; i < outOfLineCode_.length(); i++) { 1.96 + if (!gen->alloc().ensureBallast()) 1.97 + return false; 1.98 + masm.setFramePushed(outOfLineCode_[i]->framePushed()); 1.99 + lastPC_ = outOfLineCode_[i]->pc(); 1.100 + if (!sps_.prepareForOOL()) 1.101 + return false; 1.102 + sps_.setPushed(outOfLineCode_[i]->script()); 1.103 + outOfLineCode_[i]->bind(&masm); 1.104 + 1.105 + oolIns = outOfLineCode_[i]; 1.106 + if (!outOfLineCode_[i]->generate(this)) 1.107 + return false; 1.108 + sps_.finishOOL(); 1.109 + } 1.110 + oolIns = nullptr; 1.111 + 1.112 + return true; 1.113 +} 1.114 + 1.115 +bool 1.116 +CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code) 1.117 +{ 1.118 + code->setFramePushed(masm.framePushed()); 1.119 + // If an OOL instruction adds another OOL instruction, then use the original 1.120 + // instruction's script/pc instead of the basic block's that we're on 1.121 + // because they're probably not relevant any more. 1.122 + if (oolIns) 1.123 + code->setSource(oolIns->script(), oolIns->pc()); 1.124 + else 1.125 + code->setSource(current ? current->mir()->info().script() : nullptr, lastPC_); 1.126 + JS_ASSERT_IF(code->script(), code->script()->containsPC(code->pc())); 1.127 + return outOfLineCode_.append(code); 1.128 +} 1.129 + 1.130 +// see OffsetOfFrameSlot 1.131 +static inline int32_t 1.132 +ToStackIndex(LAllocation *a) 1.133 +{ 1.134 + if (a->isStackSlot()) { 1.135 + JS_ASSERT(a->toStackSlot()->slot() >= 1); 1.136 + return a->toStackSlot()->slot(); 1.137 + } 1.138 + JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index()); 1.139 + return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index()); 1.140 +} 1.141 + 1.142 +bool 1.143 +CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint, 1.144 + uint32_t *startIndex) 1.145 +{ 1.146 + IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u", 1.147 + resumePoint->numOperands(), (void *) resumePoint, *startIndex); 1.148 + for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) { 1.149 + uint32_t i = allocno + *startIndex; 1.150 + MDefinition *mir = resumePoint->getOperand(allocno); 1.151 + 1.152 + if (mir->isBox()) 1.153 + mir = mir->toBox()->getOperand(0); 1.154 + 1.155 + MIRType type = mir->isUnused() 1.156 + ? MIRType_MagicOptimizedOut 1.157 + : mir->type(); 1.158 + 1.159 + RValueAllocation alloc; 1.160 + 1.161 + switch (type) { 1.162 + case MIRType_Undefined: 1.163 + alloc = RValueAllocation::Undefined(); 1.164 + break; 1.165 + case MIRType_Null: 1.166 + alloc = RValueAllocation::Null(); 1.167 + break; 1.168 + case MIRType_Int32: 1.169 + case MIRType_String: 1.170 + case MIRType_Object: 1.171 + case MIRType_Boolean: 1.172 + case MIRType_Double: 1.173 + case MIRType_Float32: 1.174 + { 1.175 + LAllocation *payload = snapshot->payloadOfSlot(i); 1.176 + JSValueType valueType = ValueTypeFromMIRType(type); 1.177 + if (payload->isMemory()) { 1.178 + if (type == MIRType_Float32) 1.179 + alloc = RValueAllocation::Float32(ToStackIndex(payload)); 1.180 + else 1.181 + alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload)); 1.182 + } else if (payload->isGeneralReg()) { 1.183 + alloc = RValueAllocation::Typed(valueType, ToRegister(payload)); 1.184 + } else if (payload->isFloatReg()) { 1.185 + FloatRegister reg = ToFloatRegister(payload); 1.186 + if (type == MIRType_Float32) 1.187 + alloc = RValueAllocation::Float32(reg); 1.188 + else 1.189 + alloc = RValueAllocation::Double(reg); 1.190 + } else { 1.191 + MConstant *constant = mir->toConstant(); 1.192 + uint32_t index; 1.193 + if (!graph.addConstantToPool(constant->value(), &index)) 1.194 + return false; 1.195 + alloc = RValueAllocation::ConstantPool(index); 1.196 + } 1.197 + break; 1.198 + } 1.199 + case MIRType_MagicOptimizedArguments: 1.200 + case MIRType_MagicOptimizedOut: 1.201 + { 1.202 + uint32_t index; 1.203 + JSWhyMagic why = (type == MIRType_MagicOptimizedArguments 1.204 + ? JS_OPTIMIZED_ARGUMENTS 1.205 + : JS_OPTIMIZED_OUT); 1.206 + Value v = MagicValue(why); 1.207 + if (!graph.addConstantToPool(v, &index)) 1.208 + return false; 1.209 + alloc = RValueAllocation::ConstantPool(index); 1.210 + break; 1.211 + } 1.212 + default: 1.213 + { 1.214 + JS_ASSERT(mir->type() == MIRType_Value); 1.215 + LAllocation *payload = snapshot->payloadOfSlot(i); 1.216 +#ifdef JS_NUNBOX32 1.217 + LAllocation *type = snapshot->typeOfSlot(i); 1.218 + if (type->isRegister()) { 1.219 + if (payload->isRegister()) 1.220 + alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload)); 1.221 + else 1.222 + alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload)); 1.223 + } else { 1.224 + if (payload->isRegister()) 1.225 + alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload)); 1.226 + else 1.227 + alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload)); 1.228 + } 1.229 +#elif JS_PUNBOX64 1.230 + if (payload->isRegister()) 1.231 + alloc = RValueAllocation::Untyped(ToRegister(payload)); 1.232 + else 1.233 + alloc = RValueAllocation::Untyped(ToStackIndex(payload)); 1.234 +#endif 1.235 + break; 1.236 + } 1.237 + } 1.238 + 1.239 + snapshots_.add(alloc); 1.240 + } 1.241 + 1.242 + *startIndex += resumePoint->numOperands(); 1.243 + return true; 1.244 +} 1.245 + 1.246 +bool 1.247 +CodeGeneratorShared::encode(LRecoverInfo *recover) 1.248 +{ 1.249 + if (recover->recoverOffset() != INVALID_RECOVER_OFFSET) 1.250 + return true; 1.251 + 1.252 + uint32_t frameCount = recover->mir()->frameCount(); 1.253 + IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)", 1.254 + (void *)recover, frameCount); 1.255 + 1.256 + MResumePoint::Mode mode = recover->mir()->mode(); 1.257 + JS_ASSERT(mode != MResumePoint::Outer); 1.258 + bool resumeAfter = (mode == MResumePoint::ResumeAfter); 1.259 + 1.260 + RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter); 1.261 + 1.262 + for (MResumePoint **it = recover->begin(), **end = recover->end(); 1.263 + it != end; 1.264 + ++it) 1.265 + { 1.266 + if (!recovers_.writeFrame(*it)) 1.267 + return false; 1.268 + } 1.269 + 1.270 + recovers_.endRecover(); 1.271 + recover->setRecoverOffset(offset); 1.272 + return !recovers_.oom(); 1.273 +} 1.274 + 1.275 +bool 1.276 +CodeGeneratorShared::encode(LSnapshot *snapshot) 1.277 +{ 1.278 + if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET) 1.279 + return true; 1.280 + 1.281 + LRecoverInfo *recoverInfo = snapshot->recoverInfo(); 1.282 + if (!encode(recoverInfo)) 1.283 + return false; 1.284 + 1.285 + RecoverOffset recoverOffset = recoverInfo->recoverOffset(); 1.286 + MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET); 1.287 + 1.288 + IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)", 1.289 + (void *)snapshot, (void*) recoverInfo); 1.290 + 1.291 + SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind()); 1.292 + 1.293 +#ifdef TRACK_SNAPSHOTS 1.294 + uint32_t pcOpcode = 0; 1.295 + uint32_t lirOpcode = 0; 1.296 + uint32_t lirId = 0; 1.297 + uint32_t mirOpcode = 0; 1.298 + uint32_t mirId = 0; 1.299 + 1.300 + if (LInstruction *ins = instruction()) { 1.301 + lirOpcode = ins->op(); 1.302 + lirId = ins->id(); 1.303 + if (ins->mirRaw()) { 1.304 + mirOpcode = ins->mirRaw()->op(); 1.305 + mirId = ins->mirRaw()->id(); 1.306 + if (ins->mirRaw()->trackedPc()) 1.307 + pcOpcode = *ins->mirRaw()->trackedPc(); 1.308 + } 1.309 + } 1.310 + snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId); 1.311 +#endif 1.312 + 1.313 + uint32_t startIndex = 0; 1.314 + for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end(); 1.315 + it != end; 1.316 + ++it) 1.317 + { 1.318 + MResumePoint *mir = *it; 1.319 + if (!encodeAllocations(snapshot, mir, &startIndex)) 1.320 + return false; 1.321 + } 1.322 + 1.323 + MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots()); 1.324 + snapshots_.endSnapshot(); 1.325 + snapshot->setSnapshotOffset(offset); 1.326 + return !snapshots_.oom(); 1.327 +} 1.328 + 1.329 +bool 1.330 +CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot) 1.331 +{ 1.332 + JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET); 1.333 + 1.334 + // Can we not use bailout tables at all? 1.335 + if (!deoptTable_) 1.336 + return false; 1.337 + 1.338 + JS_ASSERT(frameClass_ != FrameSizeClass::None()); 1.339 + 1.340 + if (snapshot->bailoutId() != INVALID_BAILOUT_ID) 1.341 + return true; 1.342 + 1.343 + // Is the bailout table full? 1.344 + if (bailouts_.length() >= BAILOUT_TABLE_SIZE) 1.345 + return false; 1.346 + 1.347 + unsigned bailoutId = bailouts_.length(); 1.348 + snapshot->setBailoutId(bailoutId); 1.349 + IonSpew(IonSpew_Snapshots, "Assigned snapshot bailout id %u", bailoutId); 1.350 + return bailouts_.append(snapshot->snapshotOffset()); 1.351 +} 1.352 + 1.353 +void 1.354 +CodeGeneratorShared::encodeSafepoints() 1.355 +{ 1.356 + for (SafepointIndex *it = safepointIndices_.begin(), *end = safepointIndices_.end(); 1.357 + it != end; 1.358 + ++it) 1.359 + { 1.360 + LSafepoint *safepoint = it->safepoint(); 1.361 + 1.362 + if (!safepoint->encoded()) { 1.363 + safepoint->fixupOffset(&masm); 1.364 + safepoints_.encode(safepoint); 1.365 + } 1.366 + 1.367 + it->resolve(); 1.368 + } 1.369 +} 1.370 + 1.371 +bool 1.372 +CodeGeneratorShared::markSafepoint(LInstruction *ins) 1.373 +{ 1.374 + return markSafepointAt(masm.currentOffset(), ins); 1.375 +} 1.376 + 1.377 +bool 1.378 +CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction *ins) 1.379 +{ 1.380 + JS_ASSERT_IF(!safepointIndices_.empty(), 1.381 + offset - safepointIndices_.back().displacement() >= sizeof(uint32_t)); 1.382 + return safepointIndices_.append(SafepointIndex(offset, ins->safepoint())); 1.383 +} 1.384 + 1.385 +void 1.386 +CodeGeneratorShared::ensureOsiSpace() 1.387 +{ 1.388 + // For a refresher, an invalidation point is of the form: 1.389 + // 1: call <target> 1.390 + // 2: ... 1.391 + // 3: <osipoint> 1.392 + // 1.393 + // The four bytes *before* instruction 2 are overwritten with an offset. 1.394 + // Callers must ensure that the instruction itself has enough bytes to 1.395 + // support this. 1.396 + // 1.397 + // The bytes *at* instruction 3 are overwritten with an invalidation jump. 1.398 + // jump. These bytes may be in a completely different IR sequence, but 1.399 + // represent the join point of the call out of the function. 1.400 + // 1.401 + // At points where we want to ensure that invalidation won't corrupt an 1.402 + // important instruction, we make sure to pad with nops. 1.403 + if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::patchWrite_NearCallSize()) { 1.404 + int32_t paddingSize = Assembler::patchWrite_NearCallSize(); 1.405 + paddingSize -= masm.currentOffset() - lastOsiPointOffset_; 1.406 + for (int32_t i = 0; i < paddingSize; ++i) 1.407 + masm.nop(); 1.408 + } 1.409 + JS_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::patchWrite_NearCallSize()); 1.410 + lastOsiPointOffset_ = masm.currentOffset(); 1.411 +} 1.412 + 1.413 +bool 1.414 +CodeGeneratorShared::markOsiPoint(LOsiPoint *ins, uint32_t *callPointOffset) 1.415 +{ 1.416 + if (!encode(ins->snapshot())) 1.417 + return false; 1.418 + 1.419 + ensureOsiSpace(); 1.420 + 1.421 + *callPointOffset = masm.currentOffset(); 1.422 + SnapshotOffset so = ins->snapshot()->snapshotOffset(); 1.423 + return osiIndices_.append(OsiIndex(*callPointOffset, so)); 1.424 +} 1.425 + 1.426 +#ifdef CHECK_OSIPOINT_REGISTERS 1.427 +template <class Op> 1.428 +static void 1.429 +HandleRegisterDump(Op op, MacroAssembler &masm, RegisterSet liveRegs, Register activation, 1.430 + Register scratch) 1.431 +{ 1.432 + const size_t baseOffset = JitActivation::offsetOfRegs(); 1.433 + 1.434 + // Handle live GPRs. 1.435 + for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); iter++) { 1.436 + Register reg = *iter; 1.437 + Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); 1.438 + 1.439 + if (reg == activation) { 1.440 + // To use the original value of the activation register (that's 1.441 + // now on top of the stack), we need the scratch register. 1.442 + masm.push(scratch); 1.443 + masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch); 1.444 + op(scratch, dump); 1.445 + masm.pop(scratch); 1.446 + } else { 1.447 + op(reg, dump); 1.448 + } 1.449 + } 1.450 + 1.451 + // Handle live FPRs. 1.452 + for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); iter++) { 1.453 + FloatRegister reg = *iter; 1.454 + Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); 1.455 + op(reg, dump); 1.456 + } 1.457 +} 1.458 + 1.459 +class StoreOp 1.460 +{ 1.461 + MacroAssembler &masm; 1.462 + 1.463 + public: 1.464 + StoreOp(MacroAssembler &masm) 1.465 + : masm(masm) 1.466 + {} 1.467 + 1.468 + void operator()(Register reg, Address dump) { 1.469 + masm.storePtr(reg, dump); 1.470 + } 1.471 + void operator()(FloatRegister reg, Address dump) { 1.472 + masm.storeDouble(reg, dump); 1.473 + } 1.474 +}; 1.475 + 1.476 +static void 1.477 +StoreAllLiveRegs(MacroAssembler &masm, RegisterSet liveRegs) 1.478 +{ 1.479 + // Store a copy of all live registers before performing the call. 1.480 + // When we reach the OsiPoint, we can use this to check nothing 1.481 + // modified them in the meantime. 1.482 + 1.483 + // Load pointer to the JitActivation in a scratch register. 1.484 + GeneralRegisterSet allRegs(GeneralRegisterSet::All()); 1.485 + Register scratch = allRegs.takeAny(); 1.486 + masm.push(scratch); 1.487 + masm.loadJitActivation(scratch); 1.488 + 1.489 + Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); 1.490 + masm.add32(Imm32(1), checkRegs); 1.491 + 1.492 + StoreOp op(masm); 1.493 + HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); 1.494 + 1.495 + masm.pop(scratch); 1.496 +} 1.497 + 1.498 +class VerifyOp 1.499 +{ 1.500 + MacroAssembler &masm; 1.501 + Label *failure_; 1.502 + 1.503 + public: 1.504 + VerifyOp(MacroAssembler &masm, Label *failure) 1.505 + : masm(masm), failure_(failure) 1.506 + {} 1.507 + 1.508 + void operator()(Register reg, Address dump) { 1.509 + masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); 1.510 + } 1.511 + void operator()(FloatRegister reg, Address dump) { 1.512 + masm.loadDouble(dump, ScratchFloatReg); 1.513 + masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_); 1.514 + } 1.515 +}; 1.516 + 1.517 +static void 1.518 +OsiPointRegisterCheckFailed() 1.519 +{ 1.520 + // Any live register captured by a safepoint (other than temp registers) 1.521 + // must remain unchanged between the call and the OsiPoint instruction. 1.522 + MOZ_ASSUME_UNREACHABLE("Modified registers between VM call and OsiPoint"); 1.523 +} 1.524 + 1.525 +void 1.526 +CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint) 1.527 +{ 1.528 + // Ensure the live registers stored by callVM did not change between 1.529 + // the call and this OsiPoint. Try-catch relies on this invariant. 1.530 + 1.531 + // Load pointer to the JitActivation in a scratch register. 1.532 + GeneralRegisterSet allRegs(GeneralRegisterSet::All()); 1.533 + Register scratch = allRegs.takeAny(); 1.534 + masm.push(scratch); 1.535 + masm.loadJitActivation(scratch); 1.536 + 1.537 + // If we should not check registers (because the instruction did not call 1.538 + // into the VM, or a GC happened), we're done. 1.539 + Label failure, done; 1.540 + Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); 1.541 + masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); 1.542 + 1.543 + // Having more than one VM function call made in one visit function at 1.544 + // runtime is a sec-ciritcal error, because if we conservatively assume that 1.545 + // one of the function call can re-enter Ion, then the invalidation process 1.546 + // will potentially add a call at a random location, by patching the code 1.547 + // before the return address. 1.548 + masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); 1.549 + 1.550 + // Ignore clobbered registers. Some instructions (like LValueToInt32) modify 1.551 + // temps after calling into the VM. This is fine because no other 1.552 + // instructions (including this OsiPoint) will depend on them. Also 1.553 + // backtracking can also use the same register for an input and an output. 1.554 + // These are marked as clobbered and shouldn't get checked. 1.555 + RegisterSet liveRegs = safepoint->liveRegs(); 1.556 + liveRegs = RegisterSet::Intersect(liveRegs, RegisterSet::Not(safepoint->clobberedRegs())); 1.557 + 1.558 + VerifyOp op(masm, &failure); 1.559 + HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); 1.560 + 1.561 + masm.jump(&done); 1.562 + 1.563 + // Do not profile the callWithABI that occurs below. This is to avoid a 1.564 + // rare corner case that occurs when profiling interacts with itself: 1.565 + // 1.566 + // When slow profiling assertions are turned on, FunctionBoundary ops 1.567 + // (which update the profiler pseudo-stack) may emit a callVM, which 1.568 + // forces them to have an osi point associated with them. The 1.569 + // FunctionBoundary for inline function entry is added to the caller's 1.570 + // graph with a PC from the caller's code, but during codegen it modifies 1.571 + // SPS instrumentation to add the callee as the current top-most script. 1.572 + // When codegen gets to the OSIPoint, and the callWithABI below is 1.573 + // emitted, the codegen thinks that the current frame is the callee, but 1.574 + // the PC it's using from the OSIPoint refers to the caller. This causes 1.575 + // the profiler instrumentation of the callWithABI below to ASSERT, since 1.576 + // the script and pc are mismatched. To avoid this, we simply omit 1.577 + // instrumentation for these callWithABIs. 1.578 + masm.bind(&failure); 1.579 + masm.setupUnalignedABICall(0, scratch); 1.580 + masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, OsiPointRegisterCheckFailed)); 1.581 + masm.breakpoint(); 1.582 + 1.583 + masm.bind(&done); 1.584 + masm.pop(scratch); 1.585 +} 1.586 + 1.587 +bool 1.588 +CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint) 1.589 +{ 1.590 + if (!js_JitOptions.checkOsiPointRegisters) 1.591 + return false; 1.592 + 1.593 + if (gen->info().executionMode() != SequentialExecution) 1.594 + return false; 1.595 + 1.596 + if (safepoint->liveRegs().empty(true) && safepoint->liveRegs().empty(false)) 1.597 + return false; // No registers to check. 1.598 + 1.599 + return true; 1.600 +} 1.601 + 1.602 +void 1.603 +CodeGeneratorShared::resetOsiPointRegs(LSafepoint *safepoint) 1.604 +{ 1.605 + if (!shouldVerifyOsiPointRegs(safepoint)) 1.606 + return; 1.607 + 1.608 + // Set checkRegs to 0. If we perform a VM call, the instruction 1.609 + // will set it to 1. 1.610 + GeneralRegisterSet allRegs(GeneralRegisterSet::All()); 1.611 + Register scratch = allRegs.takeAny(); 1.612 + masm.push(scratch); 1.613 + masm.loadJitActivation(scratch); 1.614 + Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); 1.615 + masm.store32(Imm32(0), checkRegs); 1.616 + masm.pop(scratch); 1.617 +} 1.618 +#endif 1.619 + 1.620 +// Before doing any call to Cpp, you should ensure that volatile 1.621 +// registers are evicted by the register allocator. 1.622 +bool 1.623 +CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack) 1.624 +{ 1.625 + // Different execution modes have different sets of VM functions. 1.626 + JS_ASSERT(fun.executionMode == gen->info().executionMode()); 1.627 + 1.628 + // If we're calling a function with an out parameter type of double, make 1.629 + // sure we have an FPU. 1.630 + JS_ASSERT_IF(fun.outParam == Type_Double, GetIonContext()->runtime->jitSupportsFloatingPoint()); 1.631 + 1.632 +#ifdef DEBUG 1.633 + if (ins->mirRaw()) { 1.634 + JS_ASSERT(ins->mirRaw()->isInstruction()); 1.635 + MInstruction *mir = ins->mirRaw()->toInstruction(); 1.636 + JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint()); 1.637 + } 1.638 +#endif 1.639 + 1.640 +#ifdef JS_TRACE_LOGGING 1.641 + if (!emitTracelogStartEvent(TraceLogger::VM)) 1.642 + return false; 1.643 +#endif 1.644 + 1.645 + // Stack is: 1.646 + // ... frame ... 1.647 + // [args] 1.648 +#ifdef DEBUG 1.649 + JS_ASSERT(pushedArgs_ == fun.explicitArgs); 1.650 + pushedArgs_ = 0; 1.651 +#endif 1.652 + 1.653 + // Get the wrapper of the VM function. 1.654 + JitCode *wrapper = gen->jitRuntime()->getVMWrapper(fun); 1.655 + if (!wrapper) 1.656 + return false; 1.657 + 1.658 +#ifdef CHECK_OSIPOINT_REGISTERS 1.659 + if (shouldVerifyOsiPointRegs(ins->safepoint())) 1.660 + StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); 1.661 +#endif 1.662 + 1.663 + // Call the wrapper function. The wrapper is in charge to unwind the stack 1.664 + // when returning from the call. Failures are handled with exceptions based 1.665 + // on the return value of the C functions. To guard the outcome of the 1.666 + // returned value, use another LIR instruction. 1.667 + uint32_t callOffset; 1.668 + if (dynStack) 1.669 + callOffset = masm.callWithExitFrame(wrapper, *dynStack); 1.670 + else 1.671 + callOffset = masm.callWithExitFrame(wrapper); 1.672 + 1.673 + if (!markSafepointAt(callOffset, ins)) 1.674 + return false; 1.675 + 1.676 + // Remove rest of the frame left on the stack. We remove the return address 1.677 + // which is implicitly poped when returning. 1.678 + int framePop = sizeof(IonExitFrameLayout) - sizeof(void*); 1.679 + 1.680 + // Pop arguments from framePushed. 1.681 + masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop); 1.682 + // Stack is: 1.683 + // ... frame ... 1.684 + 1.685 +#ifdef JS_TRACE_LOGGING 1.686 + if (!emitTracelogStopEvent(TraceLogger::VM)) 1.687 + return false; 1.688 +#endif 1.689 + 1.690 + return true; 1.691 +} 1.692 + 1.693 +class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared> 1.694 +{ 1.695 + FloatRegister src_; 1.696 + Register dest_; 1.697 + bool needFloat32Conversion_; 1.698 + 1.699 + public: 1.700 + OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false) 1.701 + : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion) 1.702 + { } 1.703 + 1.704 + bool accept(CodeGeneratorShared *codegen) { 1.705 + return codegen->visitOutOfLineTruncateSlow(this); 1.706 + } 1.707 + FloatRegister src() const { 1.708 + return src_; 1.709 + } 1.710 + Register dest() const { 1.711 + return dest_; 1.712 + } 1.713 + bool needFloat32Conversion() const { 1.714 + return needFloat32Conversion_; 1.715 + } 1.716 + 1.717 +}; 1.718 + 1.719 +OutOfLineCode * 1.720 +CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest) 1.721 +{ 1.722 + OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest); 1.723 + if (!addOutOfLineCode(ool)) 1.724 + return nullptr; 1.725 + return ool; 1.726 +} 1.727 + 1.728 +bool 1.729 +CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register &dest) 1.730 +{ 1.731 + OutOfLineCode *ool = oolTruncateDouble(src, dest); 1.732 + if (!ool) 1.733 + return false; 1.734 + 1.735 + masm.branchTruncateDouble(src, dest, ool->entry()); 1.736 + masm.bind(ool->rejoin()); 1.737 + return true; 1.738 +} 1.739 + 1.740 +bool 1.741 +CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest) 1.742 +{ 1.743 + OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true); 1.744 + if (!addOutOfLineCode(ool)) 1.745 + return false; 1.746 + 1.747 + masm.branchTruncateFloat32(src, dest, ool->entry()); 1.748 + masm.bind(ool->rejoin()); 1.749 + return true; 1.750 +} 1.751 + 1.752 +bool 1.753 +CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool) 1.754 +{ 1.755 + FloatRegister src = ool->src(); 1.756 + Register dest = ool->dest(); 1.757 + 1.758 + saveVolatile(dest); 1.759 + 1.760 + if (ool->needFloat32Conversion()) { 1.761 + masm.push(src); 1.762 + masm.convertFloat32ToDouble(src, src); 1.763 + } 1.764 + 1.765 + masm.setupUnalignedABICall(1, dest); 1.766 + masm.passABIArg(src, MoveOp::DOUBLE); 1.767 + if (gen->compilingAsmJS()) 1.768 + masm.callWithABI(AsmJSImm_ToInt32); 1.769 + else 1.770 + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); 1.771 + masm.storeCallResult(dest); 1.772 + 1.773 + if (ool->needFloat32Conversion()) 1.774 + masm.pop(src); 1.775 + 1.776 + restoreVolatile(dest); 1.777 + 1.778 + masm.jump(ool->rejoin()); 1.779 + return true; 1.780 +} 1.781 + 1.782 +bool 1.783 +CodeGeneratorShared::omitOverRecursedCheck() const 1.784 +{ 1.785 + // If the current function makes no calls (which means it isn't recursive) 1.786 + // and it uses only a small amount of stack space, it doesn't need a 1.787 + // stack overflow check. Note that the actual number here is somewhat 1.788 + // arbitrary, and codegen actually uses small bounded amounts of 1.789 + // additional stack space in some cases too. 1.790 + return frameSize() < 64 && !gen->performsCall(); 1.791 +} 1.792 + 1.793 +void 1.794 +CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type) 1.795 +{ 1.796 + if (index->isConstant()) { 1.797 + Address address(base, ToInt32(index) * sizeof(Value)); 1.798 + masm.patchableCallPreBarrier(address, type); 1.799 + } else { 1.800 + BaseIndex address(base, ToRegister(index), TimesEight); 1.801 + masm.patchableCallPreBarrier(address, type); 1.802 + } 1.803 +} 1.804 + 1.805 +void 1.806 +CodeGeneratorShared::emitPreBarrier(Address address, MIRType type) 1.807 +{ 1.808 + masm.patchableCallPreBarrier(address, type); 1.809 +} 1.810 + 1.811 +void 1.812 +CodeGeneratorShared::dropArguments(unsigned argc) 1.813 +{ 1.814 + pushedArgumentSlots_.shrinkBy(argc); 1.815 +} 1.816 + 1.817 +bool 1.818 +CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint) 1.819 +{ 1.820 + for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) { 1.821 + if (!safepoint->addValueSlot(pushedArgumentSlots_[i])) 1.822 + return false; 1.823 + } 1.824 + return true; 1.825 +} 1.826 + 1.827 +OutOfLineAbortPar * 1.828 +CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock, 1.829 + jsbytecode *bytecode) 1.830 +{ 1.831 + OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode); 1.832 + if (!ool || !addOutOfLineCode(ool)) 1.833 + return nullptr; 1.834 + return ool; 1.835 +} 1.836 + 1.837 +OutOfLineAbortPar * 1.838 +CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir) 1.839 +{ 1.840 + MDefinition *mir = lir->mirRaw(); 1.841 + MBasicBlock *block = mir->block(); 1.842 + jsbytecode *pc = mir->trackedPc(); 1.843 + if (!pc) { 1.844 + if (lir->snapshot()) 1.845 + pc = lir->snapshot()->mir()->pc(); 1.846 + else 1.847 + pc = block->pc(); 1.848 + } 1.849 + return oolAbortPar(cause, block, pc); 1.850 +} 1.851 + 1.852 +OutOfLinePropagateAbortPar * 1.853 +CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir) 1.854 +{ 1.855 + OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir); 1.856 + if (!ool || !addOutOfLineCode(ool)) 1.857 + return nullptr; 1.858 + return ool; 1.859 +} 1.860 + 1.861 +bool 1.862 +OutOfLineAbortPar::generate(CodeGeneratorShared *codegen) 1.863 +{ 1.864 + codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar"); 1.865 + return codegen->visitOutOfLineAbortPar(this); 1.866 +} 1.867 + 1.868 +bool 1.869 +OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen) 1.870 +{ 1.871 + codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar"); 1.872 + return codegen->visitOutOfLinePropagateAbortPar(this); 1.873 +} 1.874 + 1.875 +bool 1.876 +CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir, 1.877 + const char *bailoutName) 1.878 +{ 1.879 + JS_ASSERT_IF(!lir, bailoutName); 1.880 + 1.881 + if (!IonSpewEnabled(IonSpew_Trace)) 1.882 + return true; 1.883 + 1.884 + uint32_t execMode = (uint32_t) gen->info().executionMode(); 1.885 + uint32_t lirIndex; 1.886 + const char *lirOpName; 1.887 + const char *mirOpName; 1.888 + JSScript *script; 1.889 + jsbytecode *pc; 1.890 + 1.891 + masm.PushRegsInMask(RegisterSet::Volatile()); 1.892 + masm.reserveStack(sizeof(IonLIRTraceData)); 1.893 + 1.894 + // This first move is here so that when you scan the disassembly, 1.895 + // you can easily pick out where each instruction begins. The 1.896 + // next few items indicate to you the Basic Block / LIR. 1.897 + masm.move32(Imm32(0xDEADBEEF), CallTempReg0); 1.898 + 1.899 + if (lir) { 1.900 + lirIndex = lir->id(); 1.901 + lirOpName = lir->opName(); 1.902 + if (MDefinition *mir = lir->mirRaw()) { 1.903 + mirOpName = mir->opName(); 1.904 + script = mir->block()->info().script(); 1.905 + pc = mir->trackedPc(); 1.906 + } else { 1.907 + mirOpName = nullptr; 1.908 + script = nullptr; 1.909 + pc = nullptr; 1.910 + } 1.911 + } else { 1.912 + blockIndex = lirIndex = 0xDEADBEEF; 1.913 + lirOpName = mirOpName = bailoutName; 1.914 + script = nullptr; 1.915 + pc = nullptr; 1.916 + } 1.917 + 1.918 + masm.store32(Imm32(blockIndex), 1.919 + Address(StackPointer, offsetof(IonLIRTraceData, blockIndex))); 1.920 + masm.store32(Imm32(lirIndex), 1.921 + Address(StackPointer, offsetof(IonLIRTraceData, lirIndex))); 1.922 + masm.store32(Imm32(execMode), 1.923 + Address(StackPointer, offsetof(IonLIRTraceData, execModeInt))); 1.924 + masm.storePtr(ImmPtr(lirOpName), 1.925 + Address(StackPointer, offsetof(IonLIRTraceData, lirOpName))); 1.926 + masm.storePtr(ImmPtr(mirOpName), 1.927 + Address(StackPointer, offsetof(IonLIRTraceData, mirOpName))); 1.928 + masm.storePtr(ImmGCPtr(script), 1.929 + Address(StackPointer, offsetof(IonLIRTraceData, script))); 1.930 + masm.storePtr(ImmPtr(pc), 1.931 + Address(StackPointer, offsetof(IonLIRTraceData, pc))); 1.932 + 1.933 + masm.movePtr(StackPointer, CallTempReg0); 1.934 + masm.setupUnalignedABICall(1, CallTempReg1); 1.935 + masm.passABIArg(CallTempReg0); 1.936 + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR)); 1.937 + 1.938 + masm.freeStack(sizeof(IonLIRTraceData)); 1.939 + masm.PopRegsInMask(RegisterSet::Volatile()); 1.940 + 1.941 + return true; 1.942 +} 1.943 + 1.944 +typedef bool (*InterruptCheckFn)(JSContext *); 1.945 +const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck); 1.946 + 1.947 +Label * 1.948 +CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir) 1.949 +{ 1.950 + // If this is a loop backedge to a loop header with an implicit interrupt 1.951 + // check, use a patchable jump. Skip this search if compiling without a 1.952 + // script for asm.js, as there will be no interrupt check instruction. 1.953 + // Due to critical edge unsplitting there may no longer be unique loop 1.954 + // backedges, so just look for any edge going to an earlier block in RPO. 1.955 + if (!gen->compilingAsmJS() && mir->isLoopHeader() && mir->id() <= current->mir()->id()) { 1.956 + for (LInstructionIterator iter = mir->lir()->begin(); iter != mir->lir()->end(); iter++) { 1.957 + if (iter->isLabel() || iter->isMoveGroup()) { 1.958 + // Continue searching for an interrupt check. 1.959 + } else if (iter->isInterruptCheckImplicit()) { 1.960 + return iter->toInterruptCheckImplicit()->oolEntry(); 1.961 + } else { 1.962 + // The interrupt check should be the first instruction in the 1.963 + // loop header other than the initial label and move groups. 1.964 + JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar()); 1.965 + return nullptr; 1.966 + } 1.967 + } 1.968 + } 1.969 + 1.970 + return nullptr; 1.971 +} 1.972 + 1.973 +void 1.974 +CodeGeneratorShared::jumpToBlock(MBasicBlock *mir) 1.975 +{ 1.976 + // No jump necessary if we can fall through to the next block. 1.977 + if (isNextBlock(mir->lir())) 1.978 + return; 1.979 + 1.980 + if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { 1.981 + // Note: the backedge is initially a jump to the next instruction. 1.982 + // It will be patched to the target block's label during link(). 1.983 + RepatchLabel rejoin; 1.984 + CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin); 1.985 + masm.bind(&rejoin); 1.986 + 1.987 + masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry))); 1.988 + } else { 1.989 + masm.jump(mir->lir()->label()); 1.990 + } 1.991 +} 1.992 + 1.993 +// This function is not used for MIPS. MIPS has branchToBlock. 1.994 +#ifndef JS_CODEGEN_MIPS 1.995 +void 1.996 +CodeGeneratorShared::jumpToBlock(MBasicBlock *mir, Assembler::Condition cond) 1.997 +{ 1.998 + if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) { 1.999 + // Note: the backedge is initially a jump to the next instruction. 1.1000 + // It will be patched to the target block's label during link(). 1.1001 + RepatchLabel rejoin; 1.1002 + CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin, cond); 1.1003 + masm.bind(&rejoin); 1.1004 + 1.1005 + masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry))); 1.1006 + } else { 1.1007 + masm.j(cond, mir->lir()->label()); 1.1008 + } 1.1009 +} 1.1010 +#endif 1.1011 + 1.1012 +size_t 1.1013 +CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *numLocs) 1.1014 +{ 1.1015 + size_t firstIndex = runtimeData_.length(); 1.1016 + size_t numLocations = 0; 1.1017 + for (CacheLocationList::iterator iter = locs.begin(); iter != locs.end(); iter++) { 1.1018 + // allocateData() ensures that sizeof(CacheLocation) is word-aligned. 1.1019 + // If this changes, we will need to pad to ensure alignment. 1.1020 + size_t curIndex = allocateData(sizeof(CacheLocation)); 1.1021 + new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script); 1.1022 + numLocations++; 1.1023 + } 1.1024 + JS_ASSERT(numLocations != 0); 1.1025 + *numLocs = numLocations; 1.1026 + return firstIndex; 1.1027 +} 1.1028 + 1.1029 +ReciprocalMulConstants 1.1030 +CodeGeneratorShared::computeDivisionConstants(int d) { 1.1031 + // In what follows, d is positive and is not a power of 2. 1.1032 + JS_ASSERT(d > 0 && (d & (d - 1)) != 0); 1.1033 + 1.1034 + // Speeding up division by non power-of-2 constants is possible by 1.1035 + // calculating, during compilation, a value M such that high-order 1.1036 + // bits of M*n correspond to the result of the division. Formally, 1.1037 + // we compute values 0 <= M < 2^32 and 0 <= s < 31 such that 1.1038 + // (M * n) >> (32 + s) = floor(n/d) if n >= 0 1.1039 + // (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0. 1.1040 + // The original presentation of this technique appears in Hacker's 1.1041 + // Delight, a book by Henry S. Warren, Jr.. A proof of correctness 1.1042 + // for our version follows. 1.1043 + 1.1044 + // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies 1.1045 + // M - 2^p/d <= 2^(s+1)/d. (1) 1.1046 + // (Observe that s = FloorLog32(d) satisfies this, because in this 1.1047 + // case d <= 2^(s+1) and so the RHS of (1) is at least one). Then, 1.1048 + // 1.1049 + // a) If s <= FloorLog32(d), then M <= 2^32 - 1. 1.1050 + // Proof: Indeed, M is monotone in s and, for s = FloorLog32(d), 1.1051 + // the inequalities 2^31 > d >= 2^s + 1 readily imply 1.1052 + // 2^p / d = 2^p/(d - 1) * (d - 1)/d 1.1053 + // <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2. 1.1054 + // The claim follows by applying the ceiling function. 1.1055 + // 1.1056 + // b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d). 1.1057 + // Proof: Put x = floor(Mn/2^p); it's the unique integer for which 1.1058 + // Mn/2^p - 1 < x <= Mn/2^p. (2) 1.1059 + // Using M >= 2^p/d on the LHS and (1) on the RHS, we get 1.1060 + // n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d. 1.1061 + // Since x is an integer, it's not in the interval (n/d, (n+1)/d), 1.1062 + // and so n/d - 1 < x <= n/d, which implies x = floor(n/d). 1.1063 + // 1.1064 + // c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d). 1.1065 + // Proof: The proof is similar. Equation (2) holds as above. Using 1.1066 + // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS, 1.1067 + // n/d + n/(2^31 d) - 1 < x < n/d. 1.1068 + // Using n >= -2^31 and summing 1, 1.1069 + // n/d - 1/d < x + 1 < n/d + 1. 1.1070 + // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1. 1.1071 + // In other words, x + 1 = ceil(n/d). 1.1072 + // 1.1073 + // Condition (1) isn't necessary for the existence of M and s with 1.1074 + // the properties above. Hacker's Delight provides a slightly less 1.1075 + // restrictive condition when d >= 196611, at the cost of a 3-page 1.1076 + // proof of correctness. 1.1077 + 1.1078 + // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as 1.1079 + // 2^(s+1) >= d - (2^p)%d. 1.1080 + // We now compute the least s with this property... 1.1081 + 1.1082 + int32_t shift = 0; 1.1083 + while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d) 1.1084 + shift++; 1.1085 + 1.1086 + // ...and the corresponding M. This may not fit in a signed 32-bit 1.1087 + // integer; we will compute (M - 2^32) * n + (2^32 * n) instead of 1.1088 + // M * n if this is the case (cf. item (a) above). 1.1089 + ReciprocalMulConstants rmc; 1.1090 + rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1); 1.1091 + rmc.shiftAmount = shift; 1.1092 + 1.1093 + return rmc; 1.1094 +} 1.1095 + 1.1096 + 1.1097 +#ifdef JS_TRACE_LOGGING 1.1098 + 1.1099 +bool 1.1100 +CodeGeneratorShared::emitTracelogScript(bool isStart) 1.1101 +{ 1.1102 + RegisterSet regs = RegisterSet::Volatile(); 1.1103 + Register logger = regs.takeGeneral(); 1.1104 + Register script = regs.takeGeneral(); 1.1105 + 1.1106 + masm.Push(logger); 1.1107 + masm.Push(script); 1.1108 + 1.1109 + CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger); 1.1110 + if (!patchableTraceLoggers_.append(patchLogger)) 1.1111 + return false; 1.1112 + 1.1113 + CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script); 1.1114 + if (!patchableTLScripts_.append(patchScript)) 1.1115 + return false; 1.1116 + 1.1117 + if (isStart) 1.1118 + masm.tracelogStart(logger, script); 1.1119 + else 1.1120 + masm.tracelogStop(logger, script); 1.1121 + 1.1122 + masm.Pop(script); 1.1123 + masm.Pop(logger); 1.1124 + return true; 1.1125 +} 1.1126 + 1.1127 +bool 1.1128 +CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) 1.1129 +{ 1.1130 + if (!TraceLogTextIdEnabled(textId)) 1.1131 + return true; 1.1132 + 1.1133 + RegisterSet regs = RegisterSet::Volatile(); 1.1134 + Register logger = regs.takeGeneral(); 1.1135 + 1.1136 + masm.Push(logger); 1.1137 + 1.1138 + CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger); 1.1139 + if (!patchableTraceLoggers_.append(patchLocation)) 1.1140 + return false; 1.1141 + 1.1142 + if (isStart) { 1.1143 + masm.tracelogStart(logger, textId); 1.1144 + } else { 1.1145 +#ifdef DEBUG 1.1146 + masm.tracelogStop(logger, textId); 1.1147 +#else 1.1148 + masm.tracelogStop(logger); 1.1149 +#endif 1.1150 + } 1.1151 + 1.1152 + masm.Pop(logger); 1.1153 + return true; 1.1154 +} 1.1155 +#endif 1.1156 + 1.1157 +} // namespace jit 1.1158 +} // namespace js