1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/LIR.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,450 @@ 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/LIR.h" 1.11 + 1.12 +#include <ctype.h> 1.13 + 1.14 +#include "jsprf.h" 1.15 + 1.16 +#include "jit/IonSpewer.h" 1.17 +#include "jit/MIR.h" 1.18 +#include "jit/MIRGenerator.h" 1.19 + 1.20 +using namespace js; 1.21 +using namespace js::jit; 1.22 + 1.23 +LIRGraph::LIRGraph(MIRGraph *mir) 1.24 + : blocks_(mir->alloc()), 1.25 + constantPool_(mir->alloc()), 1.26 + constantPoolMap_(mir->alloc()), 1.27 + safepoints_(mir->alloc()), 1.28 + nonCallSafepoints_(mir->alloc()), 1.29 + numVirtualRegisters_(0), 1.30 + numInstructions_(1), // First id is 1. 1.31 + localSlotCount_(0), 1.32 + argumentSlotCount_(0), 1.33 + entrySnapshot_(nullptr), 1.34 + osrBlock_(nullptr), 1.35 + mir_(*mir) 1.36 +{ 1.37 +} 1.38 + 1.39 +bool 1.40 +LIRGraph::addConstantToPool(const Value &v, uint32_t *index) 1.41 +{ 1.42 + JS_ASSERT(constantPoolMap_.initialized()); 1.43 + 1.44 + ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v); 1.45 + if (p) { 1.46 + *index = p->value(); 1.47 + return true; 1.48 + } 1.49 + *index = constantPool_.length(); 1.50 + return constantPool_.append(v) && constantPoolMap_.add(p, v, *index); 1.51 +} 1.52 + 1.53 +bool 1.54 +LIRGraph::noteNeedsSafepoint(LInstruction *ins) 1.55 +{ 1.56 + // Instructions with safepoints must be in linear order. 1.57 + JS_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id()); 1.58 + if (!ins->isCall() && !nonCallSafepoints_.append(ins)) 1.59 + return false; 1.60 + return safepoints_.append(ins); 1.61 +} 1.62 + 1.63 +void 1.64 +LIRGraph::removeBlock(size_t i) 1.65 +{ 1.66 + blocks_.erase(blocks_.begin() + i); 1.67 +} 1.68 + 1.69 +uint32_t 1.70 +LBlock::firstId() 1.71 +{ 1.72 + if (phis_.length()) { 1.73 + return phis_[0]->id(); 1.74 + } else { 1.75 + for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) { 1.76 + if (i->id()) 1.77 + return i->id(); 1.78 + } 1.79 + } 1.80 + return 0; 1.81 +} 1.82 +uint32_t 1.83 +LBlock::lastId() 1.84 +{ 1.85 + LInstruction *last = *instructions_.rbegin(); 1.86 + JS_ASSERT(last->id()); 1.87 + // The last instruction is a control flow instruction which does not have 1.88 + // any output. 1.89 + JS_ASSERT(last->numDefs() == 0); 1.90 + return last->id(); 1.91 +} 1.92 + 1.93 +LMoveGroup * 1.94 +LBlock::getEntryMoveGroup(TempAllocator &alloc) 1.95 +{ 1.96 + if (entryMoveGroup_) 1.97 + return entryMoveGroup_; 1.98 + entryMoveGroup_ = LMoveGroup::New(alloc); 1.99 + if (begin()->isLabel()) 1.100 + insertAfter(*begin(), entryMoveGroup_); 1.101 + else 1.102 + insertBefore(*begin(), entryMoveGroup_); 1.103 + return entryMoveGroup_; 1.104 +} 1.105 + 1.106 +LMoveGroup * 1.107 +LBlock::getExitMoveGroup(TempAllocator &alloc) 1.108 +{ 1.109 + if (exitMoveGroup_) 1.110 + return exitMoveGroup_; 1.111 + exitMoveGroup_ = LMoveGroup::New(alloc); 1.112 + insertBefore(*rbegin(), exitMoveGroup_); 1.113 + return exitMoveGroup_; 1.114 +} 1.115 + 1.116 +static size_t 1.117 +TotalOperandCount(MResumePoint *mir) 1.118 +{ 1.119 + size_t accum = mir->numOperands(); 1.120 + while ((mir = mir->caller())) 1.121 + accum += mir->numOperands(); 1.122 + return accum; 1.123 +} 1.124 + 1.125 +LRecoverInfo::LRecoverInfo(TempAllocator &alloc) 1.126 + : instructions_(alloc), 1.127 + recoverOffset_(INVALID_RECOVER_OFFSET) 1.128 +{ } 1.129 + 1.130 +LRecoverInfo * 1.131 +LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir) 1.132 +{ 1.133 + LRecoverInfo *recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc()); 1.134 + if (!recoverInfo || !recoverInfo->init(mir)) 1.135 + return nullptr; 1.136 + 1.137 + IonSpew(IonSpew_Snapshots, "Generating LIR recover info %p from MIR (%p)", 1.138 + (void *)recoverInfo, (void *)mir); 1.139 + 1.140 + return recoverInfo; 1.141 +} 1.142 + 1.143 +bool 1.144 +LRecoverInfo::init(MResumePoint *rp) 1.145 +{ 1.146 + MResumePoint *it = rp; 1.147 + 1.148 + // Sort operations in the order in which we need to restore the stack. This 1.149 + // implies that outer frames, as well as operations needed to recover the 1.150 + // current frame, are located before the current frame. The inner-most 1.151 + // resume point should be the last element in the list. 1.152 + do { 1.153 + if (!instructions_.append(it)) 1.154 + return false; 1.155 + it = it->caller(); 1.156 + } while (it); 1.157 + 1.158 + Reverse(instructions_.begin(), instructions_.end()); 1.159 + MOZ_ASSERT(mir() == rp); 1.160 + return true; 1.161 +} 1.162 + 1.163 +LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind) 1.164 + : numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES), 1.165 + slots_(nullptr), 1.166 + recoverInfo_(recoverInfo), 1.167 + snapshotOffset_(INVALID_SNAPSHOT_OFFSET), 1.168 + bailoutId_(INVALID_BAILOUT_ID), 1.169 + bailoutKind_(kind) 1.170 +{ } 1.171 + 1.172 +bool 1.173 +LSnapshot::init(MIRGenerator *gen) 1.174 +{ 1.175 + slots_ = gen->allocate<LAllocation>(numSlots_); 1.176 + return !!slots_; 1.177 +} 1.178 + 1.179 +LSnapshot * 1.180 +LSnapshot::New(MIRGenerator *gen, LRecoverInfo *recover, BailoutKind kind) 1.181 +{ 1.182 + LSnapshot *snapshot = new(gen->alloc()) LSnapshot(recover, kind); 1.183 + if (!snapshot || !snapshot->init(gen)) 1.184 + return nullptr; 1.185 + 1.186 + IonSpew(IonSpew_Snapshots, "Generating LIR snapshot %p from recover (%p)", 1.187 + (void *)snapshot, (void *)recover); 1.188 + 1.189 + return snapshot; 1.190 +} 1.191 + 1.192 +void 1.193 +LSnapshot::rewriteRecoveredInput(LUse input) 1.194 +{ 1.195 + // Mark any operands to this snapshot with the same value as input as being 1.196 + // equal to the instruction's result. 1.197 + for (size_t i = 0; i < numEntries(); i++) { 1.198 + if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) 1.199 + setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT)); 1.200 + } 1.201 +} 1.202 + 1.203 +LPhi * 1.204 +LPhi::New(MIRGenerator *gen, MPhi *ins) 1.205 +{ 1.206 + LPhi *phi = new (gen->alloc()) LPhi(); 1.207 + LAllocation *inputs = gen->allocate<LAllocation>(ins->numOperands()); 1.208 + if (!inputs) 1.209 + return nullptr; 1.210 + 1.211 + phi->inputs_ = inputs; 1.212 + phi->setMir(ins); 1.213 + return phi; 1.214 +} 1.215 + 1.216 +void 1.217 +LInstruction::printName(FILE *fp, Opcode op) 1.218 +{ 1.219 + static const char * const names[] = 1.220 + { 1.221 +#define LIROP(x) #x, 1.222 + LIR_OPCODE_LIST(LIROP) 1.223 +#undef LIROP 1.224 + }; 1.225 + const char *name = names[op]; 1.226 + size_t len = strlen(name); 1.227 + for (size_t i = 0; i < len; i++) 1.228 + fprintf(fp, "%c", tolower(name[i])); 1.229 +} 1.230 + 1.231 +void 1.232 +LInstruction::printName(FILE *fp) 1.233 +{ 1.234 + printName(fp, op()); 1.235 +} 1.236 + 1.237 +static const char * const TypeChars[] = 1.238 +{ 1.239 + "g", // GENERAL 1.240 + "i", // INT32 1.241 + "o", // OBJECT 1.242 + "s", // SLOTS 1.243 + "f", // FLOAT32 1.244 + "d", // DOUBLE 1.245 +#ifdef JS_NUNBOX32 1.246 + "t", // TYPE 1.247 + "p" // PAYLOAD 1.248 +#elif JS_PUNBOX64 1.249 + "x" // BOX 1.250 +#endif 1.251 +}; 1.252 + 1.253 +static void 1.254 +PrintDefinition(FILE *fp, const LDefinition &def) 1.255 +{ 1.256 + fprintf(fp, "[%s", TypeChars[def.type()]); 1.257 + if (def.virtualRegister()) 1.258 + fprintf(fp, ":%d", def.virtualRegister()); 1.259 + if (def.policy() == LDefinition::PRESET) { 1.260 + fprintf(fp, " (%s)", def.output()->toString()); 1.261 + } else if (def.policy() == LDefinition::MUST_REUSE_INPUT) { 1.262 + fprintf(fp, " (!)"); 1.263 + } else if (def.policy() == LDefinition::PASSTHROUGH) { 1.264 + fprintf(fp, " (-)"); 1.265 + } 1.266 + fprintf(fp, "]"); 1.267 +} 1.268 + 1.269 +#ifdef DEBUG 1.270 +static void 1.271 +PrintUse(char *buf, size_t size, const LUse *use) 1.272 +{ 1.273 + switch (use->policy()) { 1.274 + case LUse::REGISTER: 1.275 + JS_snprintf(buf, size, "v%d:r", use->virtualRegister()); 1.276 + break; 1.277 + case LUse::FIXED: 1.278 + // Unfortunately, we don't know here whether the virtual register is a 1.279 + // float or a double. Should we steal a bit in LUse for help? For now, 1.280 + // nothing defines any fixed xmm registers. 1.281 + JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(), 1.282 + Registers::GetName(Registers::Code(use->registerCode()))); 1.283 + break; 1.284 + case LUse::ANY: 1.285 + JS_snprintf(buf, size, "v%d:r?", use->virtualRegister()); 1.286 + break; 1.287 + case LUse::KEEPALIVE: 1.288 + JS_snprintf(buf, size, "v%d:*", use->virtualRegister()); 1.289 + break; 1.290 + case LUse::RECOVERED_INPUT: 1.291 + JS_snprintf(buf, size, "v%d:**", use->virtualRegister()); 1.292 + break; 1.293 + default: 1.294 + MOZ_ASSUME_UNREACHABLE("invalid use policy"); 1.295 + } 1.296 +} 1.297 + 1.298 +const char * 1.299 +LAllocation::toString() const 1.300 +{ 1.301 + // Not reentrant! 1.302 + static char buf[40]; 1.303 + 1.304 + switch (kind()) { 1.305 + case LAllocation::CONSTANT_VALUE: 1.306 + case LAllocation::CONSTANT_INDEX: 1.307 + return "c"; 1.308 + case LAllocation::GPR: 1.309 + JS_snprintf(buf, sizeof(buf), "=%s", toGeneralReg()->reg().name()); 1.310 + return buf; 1.311 + case LAllocation::FPU: 1.312 + JS_snprintf(buf, sizeof(buf), "=%s", toFloatReg()->reg().name()); 1.313 + return buf; 1.314 + case LAllocation::STACK_SLOT: 1.315 + JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot()); 1.316 + return buf; 1.317 + case LAllocation::ARGUMENT_SLOT: 1.318 + JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); 1.319 + return buf; 1.320 + case LAllocation::USE: 1.321 + PrintUse(buf, sizeof(buf), toUse()); 1.322 + return buf; 1.323 + default: 1.324 + MOZ_ASSUME_UNREACHABLE("what?"); 1.325 + } 1.326 +} 1.327 +#endif // DEBUG 1.328 + 1.329 +void 1.330 +LAllocation::dump() const 1.331 +{ 1.332 + fprintf(stderr, "%s\n", toString()); 1.333 +} 1.334 + 1.335 +void 1.336 +LInstruction::printOperands(FILE *fp) 1.337 +{ 1.338 + for (size_t i = 0, e = numOperands(); i < e; i++) { 1.339 + fprintf(fp, " (%s)", getOperand(i)->toString()); 1.340 + if (i != numOperands() - 1) 1.341 + fprintf(fp, ","); 1.342 + } 1.343 +} 1.344 + 1.345 +void 1.346 +LInstruction::assignSnapshot(LSnapshot *snapshot) 1.347 +{ 1.348 + JS_ASSERT(!snapshot_); 1.349 + snapshot_ = snapshot; 1.350 + 1.351 +#ifdef DEBUG 1.352 + if (IonSpewEnabled(IonSpew_Snapshots)) { 1.353 + IonSpewHeader(IonSpew_Snapshots); 1.354 + fprintf(IonSpewFile, "Assigning snapshot %p to instruction %p (", 1.355 + (void *)snapshot, (void *)this); 1.356 + printName(IonSpewFile); 1.357 + fprintf(IonSpewFile, ")\n"); 1.358 + } 1.359 +#endif 1.360 +} 1.361 + 1.362 +void 1.363 +LInstruction::dump(FILE *fp) 1.364 +{ 1.365 + fprintf(fp, "{"); 1.366 + for (size_t i = 0; i < numDefs(); i++) { 1.367 + PrintDefinition(fp, *getDef(i)); 1.368 + if (i != numDefs() - 1) 1.369 + fprintf(fp, ", "); 1.370 + } 1.371 + fprintf(fp, "} <- "); 1.372 + 1.373 + printName(fp); 1.374 + 1.375 + 1.376 + printInfo(fp); 1.377 + 1.378 + if (numTemps()) { 1.379 + fprintf(fp, " t=("); 1.380 + for (size_t i = 0; i < numTemps(); i++) { 1.381 + PrintDefinition(fp, *getTemp(i)); 1.382 + if (i != numTemps() - 1) 1.383 + fprintf(fp, ", "); 1.384 + } 1.385 + fprintf(fp, ")"); 1.386 + } 1.387 + fprintf(fp, "\n"); 1.388 +} 1.389 + 1.390 +void 1.391 +LInstruction::dump() 1.392 +{ 1.393 + return dump(stderr); 1.394 +} 1.395 + 1.396 +void 1.397 +LInstruction::initSafepoint(TempAllocator &alloc) 1.398 +{ 1.399 + JS_ASSERT(!safepoint_); 1.400 + safepoint_ = new(alloc) LSafepoint(alloc); 1.401 + JS_ASSERT(safepoint_); 1.402 +} 1.403 + 1.404 +bool 1.405 +LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type) 1.406 +{ 1.407 +#ifdef DEBUG 1.408 + JS_ASSERT(*from != *to); 1.409 + for (size_t i = 0; i < moves_.length(); i++) 1.410 + JS_ASSERT(*to != *moves_[i].to()); 1.411 +#endif 1.412 + return moves_.append(LMove(from, to, type)); 1.413 +} 1.414 + 1.415 +bool 1.416 +LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type) 1.417 +{ 1.418 + // Transform the operands to this move so that performing the result 1.419 + // simultaneously with existing moves in the group will have the same 1.420 + // effect as if the original move took place after the existing moves. 1.421 + 1.422 + for (size_t i = 0; i < moves_.length(); i++) { 1.423 + if (*moves_[i].to() == *from) { 1.424 + from = moves_[i].from(); 1.425 + break; 1.426 + } 1.427 + } 1.428 + 1.429 + if (*from == *to) 1.430 + return true; 1.431 + 1.432 + for (size_t i = 0; i < moves_.length(); i++) { 1.433 + if (*to == *moves_[i].to()) { 1.434 + moves_[i] = LMove(from, to, type); 1.435 + return true; 1.436 + } 1.437 + } 1.438 + 1.439 + return add(from, to, type); 1.440 +} 1.441 + 1.442 +void 1.443 +LMoveGroup::printOperands(FILE *fp) 1.444 +{ 1.445 + for (size_t i = 0; i < numMoves(); i++) { 1.446 + const LMove &move = getMove(i); 1.447 + // Use two printfs, as LAllocation::toString is not reentrant. 1.448 + fprintf(fp, "[%s", move.from()->toString()); 1.449 + fprintf(fp, " -> %s]", move.to()->toString()); 1.450 + if (i != numMoves() - 1) 1.451 + fprintf(fp, ", "); 1.452 + } 1.453 +}