LCOV - code coverage report
Current view: top level - js/src/jit - LIR.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 149 318 46.9 %
Date: 2017-07-14 16:53:18 Functions: 25 40 62.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jit/LIR.h"
       8             : 
       9             : #include <ctype.h>
      10             : 
      11             : #include "jsprf.h"
      12             : 
      13             : #include "jit/JitSpewer.h"
      14             : #include "jit/MIR.h"
      15             : #include "jit/MIRGenerator.h"
      16             : 
      17             : using namespace js;
      18             : using namespace js::jit;
      19             : 
      20           8 : LIRGraph::LIRGraph(MIRGraph* mir)
      21             :   : blocks_(),
      22             :     constantPool_(mir->alloc()),
      23             :     constantPoolMap_(mir->alloc()),
      24             :     safepoints_(mir->alloc()),
      25             :     nonCallSafepoints_(mir->alloc()),
      26             :     numVirtualRegisters_(0),
      27             :     numInstructions_(1), // First id is 1.
      28             :     localSlotCount_(0),
      29             :     argumentSlotCount_(0),
      30             :     entrySnapshot_(nullptr),
      31           8 :     mir_(*mir)
      32             : {
      33           8 : }
      34             : 
      35             : bool
      36         766 : LIRGraph::addConstantToPool(const Value& v, uint32_t* index)
      37             : {
      38         766 :     MOZ_ASSERT(constantPoolMap_.initialized());
      39             : 
      40         766 :     ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v);
      41         766 :     if (p) {
      42         743 :         *index = p->value();
      43         743 :         return true;
      44             :     }
      45          23 :     *index = constantPool_.length();
      46          23 :     return constantPool_.append(v) && constantPoolMap_.add(p, v, *index);
      47             : }
      48             : 
      49             : bool
      50         134 : LIRGraph::noteNeedsSafepoint(LInstruction* ins)
      51             : {
      52             :     // Instructions with safepoints must be in linear order.
      53         134 :     MOZ_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id());
      54         134 :     if (!ins->isCall() && !nonCallSafepoints_.append(ins))
      55           0 :         return false;
      56         134 :     return safepoints_.append(ins);
      57             : }
      58             : 
      59             : void
      60           0 : LIRGraph::dump(GenericPrinter& out)
      61             : {
      62           0 :     for (size_t i = 0; i < numBlocks(); i++) {
      63           0 :         getBlock(i)->dump(out);
      64           0 :         out.printf("\n");
      65             :     }
      66           0 : }
      67             : 
      68             : void
      69           0 : LIRGraph::dump()
      70             : {
      71           0 :     Fprinter out(stderr);
      72           0 :     dump(out);
      73           0 :     out.finish();
      74           0 : }
      75             : 
      76         403 : LBlock::LBlock(MBasicBlock* from)
      77             :   : block_(from),
      78             :     phis_(),
      79             :     entryMoveGroup_(nullptr),
      80         403 :     exitMoveGroup_(nullptr)
      81             : {
      82         403 :     from->assignLir(this);
      83         403 : }
      84             : 
      85             : bool
      86         403 : LBlock::init(TempAllocator& alloc)
      87             : {
      88             :     // Count the number of LPhis we'll need.
      89         403 :     size_t numLPhis = 0;
      90         578 :     for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
      91         175 :         MPhi* phi = *i;
      92         175 :         switch (phi->type()) {
      93          86 :           case MIRType::Value: numLPhis += BOX_PIECES; break;
      94           0 :           case MIRType::Int64: numLPhis += INT64_PIECES; break;
      95          89 :           default: numLPhis += 1; break;
      96             :         }
      97             :     }
      98             : 
      99             :     // Allocate space for the LPhis.
     100         403 :     if (!phis_.init(alloc, numLPhis))
     101           0 :         return false;
     102             : 
     103             :     // For each MIR phi, set up LIR phis as appropriate. We'll fill in their
     104             :     // operands on each incoming edge, and set their definitions at the start of
     105             :     // their defining block.
     106         403 :     size_t phiIndex = 0;
     107         403 :     size_t numPreds = block_->numPredecessors();
     108         578 :     for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
     109         175 :         MPhi* phi = *i;
     110         175 :         MOZ_ASSERT(phi->numOperands() == numPreds);
     111             : 
     112             :         int numPhis;
     113         175 :         switch (phi->type()) {
     114          86 :           case MIRType::Value: numPhis = BOX_PIECES; break;
     115           0 :           case MIRType::Int64: numPhis = INT64_PIECES; break;
     116          89 :           default: numPhis = 1; break;
     117             :         }
     118         350 :         for (int i = 0; i < numPhis; i++) {
     119         175 :             LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds);
     120         175 :             if (!inputs)
     121           0 :                 return false;
     122             : 
     123         175 :             void* addr = &phis_[phiIndex++];
     124         175 :             LPhi* lphi = new (addr) LPhi(phi, inputs);
     125         175 :             lphi->setBlock(this);
     126             :         }
     127             :     }
     128         403 :     return true;
     129             : }
     130             : 
     131             : const LInstruction*
     132         341 : LBlock::firstInstructionWithId() const
     133             : {
     134         341 :     for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); ++i) {
     135         341 :         if (i->id())
     136         341 :             return *i;
     137             :     }
     138           0 :     return 0;
     139             : }
     140             : 
     141             : LMoveGroup*
     142         199 : LBlock::getEntryMoveGroup(TempAllocator& alloc)
     143             : {
     144         199 :     if (entryMoveGroup_)
     145          82 :         return entryMoveGroup_;
     146         117 :     entryMoveGroup_ = LMoveGroup::New(alloc);
     147         117 :     insertBefore(*begin(), entryMoveGroup_);
     148         117 :     return entryMoveGroup_;
     149             : }
     150             : 
     151             : LMoveGroup*
     152          61 : LBlock::getExitMoveGroup(TempAllocator& alloc)
     153             : {
     154          61 :     if (exitMoveGroup_)
     155          32 :         return exitMoveGroup_;
     156          29 :     exitMoveGroup_ = LMoveGroup::New(alloc);
     157          29 :     insertBefore(*rbegin(), exitMoveGroup_);
     158          29 :     return exitMoveGroup_;
     159             : }
     160             : 
     161             : void
     162           0 : LBlock::dump(GenericPrinter& out)
     163             : {
     164           0 :     out.printf("block%u:\n", mir()->id());
     165           0 :     for (size_t i = 0; i < numPhis(); ++i) {
     166           0 :         getPhi(i)->dump(out);
     167           0 :         out.printf("\n");
     168             :     }
     169           0 :     for (LInstructionIterator iter = begin(); iter != end(); iter++) {
     170           0 :         iter->dump(out);
     171           0 :         out.printf("\n");
     172             :     }
     173           0 : }
     174             : 
     175             : void
     176           0 : LBlock::dump()
     177             : {
     178           0 :     Fprinter out(stderr);
     179           0 :     dump(out);
     180           0 :     out.finish();
     181           0 : }
     182             : 
     183             : static size_t
     184         326 : TotalOperandCount(LRecoverInfo* recoverInfo)
     185             : {
     186         326 :     size_t accum = 0;
     187        7647 :     for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
     188        7321 :         if (!it->isRecoveredOnBailout())
     189        7013 :             accum++;
     190             :     }
     191         326 :     return accum;
     192             : }
     193             : 
     194         177 : LRecoverInfo::LRecoverInfo(TempAllocator& alloc)
     195             :   : instructions_(alloc),
     196         177 :     recoverOffset_(INVALID_RECOVER_OFFSET)
     197         177 : { }
     198             : 
     199             : LRecoverInfo*
     200         177 : LRecoverInfo::New(MIRGenerator* gen, MResumePoint* mir)
     201             : {
     202         177 :     LRecoverInfo* recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc());
     203         177 :     if (!recoverInfo || !recoverInfo->init(mir))
     204           0 :         return nullptr;
     205             : 
     206             :     JitSpew(JitSpew_IonSnapshots, "Generating LIR recover info %p from MIR (%p)",
     207         177 :             (void*)recoverInfo, (void*)mir);
     208             : 
     209         177 :     return recoverInfo;
     210             : }
     211             : 
     212             : bool
     213         409 : LRecoverInfo::appendOperands(MNode* ins)
     214             : {
     215        4338 :     for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
     216        3929 :         MDefinition* def = ins->getOperand(i);
     217             : 
     218             :         // As there is no cycle in the data-flow (without MPhi), checking for
     219             :         // isInWorkList implies that the definition is already in the
     220             :         // instruction vector, and not processed by a caller of the current
     221             :         // function.
     222        3929 :         if (def->isRecoveredOnBailout() && !def->isInWorklist()) {
     223          98 :             if (!appendDefinition(def))
     224           0 :                 return false;
     225             :         }
     226             :     }
     227             : 
     228         409 :     return true;
     229             : }
     230             : 
     231             : bool
     232         196 : LRecoverInfo::appendDefinition(MDefinition* def)
     233             : {
     234         196 :     MOZ_ASSERT(def->isRecoveredOnBailout());
     235         196 :     def->setInWorklist();
     236             : 
     237         196 :     if (!appendOperands(def))
     238           0 :         return false;
     239         196 :     return instructions_.append(def);
     240             : }
     241             : 
     242             : bool
     243         213 : LRecoverInfo::appendResumePoint(MResumePoint* rp)
     244             : {
     245             :     // Stores should be recovered first.
     246         311 :     for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end; ++iter) {
     247          98 :         if (!appendDefinition(iter->operand))
     248           0 :             return false;
     249             :     }
     250             : 
     251         213 :     if (rp->caller() && !appendResumePoint(rp->caller()))
     252           0 :         return false;
     253             : 
     254         213 :     if (!appendOperands(rp))
     255           0 :         return false;
     256             : 
     257         213 :     return instructions_.append(rp);
     258             : }
     259             : 
     260             : bool
     261         177 : LRecoverInfo::init(MResumePoint* rp)
     262             : {
     263             :     // Sort operations in the order in which we need to restore the stack. This
     264             :     // implies that outer frames, as well as operations needed to recover the
     265             :     // current frame, are located before the current frame. The inner-most
     266             :     // resume point should be the last element in the list.
     267         177 :     if (!appendResumePoint(rp))
     268           0 :         return false;
     269             : 
     270             :     // Remove temporary flags from all definitions.
     271         586 :     for (MNode** it = begin(); it != end(); it++) {
     272         409 :         if (!(*it)->isDefinition())
     273         213 :             continue;
     274             : 
     275         196 :         (*it)->toDefinition()->setNotInWorklist();
     276             :     }
     277             : 
     278         177 :     MOZ_ASSERT(mir() == rp);
     279         177 :     return true;
     280             : }
     281             : 
     282         326 : LSnapshot::LSnapshot(LRecoverInfo* recoverInfo, BailoutKind kind)
     283         326 :   : numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
     284             :     slots_(nullptr),
     285             :     recoverInfo_(recoverInfo),
     286             :     snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
     287             :     bailoutId_(INVALID_BAILOUT_ID),
     288         652 :     bailoutKind_(kind)
     289         326 : { }
     290             : 
     291             : bool
     292         326 : LSnapshot::init(MIRGenerator* gen)
     293             : {
     294         326 :     slots_ = gen->allocate<LAllocation>(numSlots_);
     295         326 :     return !!slots_;
     296             : }
     297             : 
     298             : LSnapshot*
     299         326 : LSnapshot::New(MIRGenerator* gen, LRecoverInfo* recover, BailoutKind kind)
     300             : {
     301         326 :     LSnapshot* snapshot = new(gen->alloc()) LSnapshot(recover, kind);
     302         326 :     if (!snapshot || !snapshot->init(gen))
     303           0 :         return nullptr;
     304             : 
     305             :     JitSpew(JitSpew_IonSnapshots, "Generating LIR snapshot %p from recover (%p)",
     306         326 :             (void*)snapshot, (void*)recover);
     307             : 
     308         326 :     return snapshot;
     309             : }
     310             : 
     311             : void
     312           1 : LSnapshot::rewriteRecoveredInput(LUse input)
     313             : {
     314             :     // Mark any operands to this snapshot with the same value as input as being
     315             :     // equal to the instruction's result.
     316          16 :     for (size_t i = 0; i < numEntries(); i++) {
     317          15 :         if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister())
     318           1 :             setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT));
     319             :     }
     320           1 : }
     321             : 
     322             : void
     323           0 : LNode::printName(GenericPrinter& out, Opcode op)
     324             : {
     325             :     static const char * const names[] =
     326             :     {
     327             : #define LIROP(x) #x,
     328             :         LIR_OPCODE_LIST(LIROP)
     329             : #undef LIROP
     330             :     };
     331           0 :     const char* name = names[op];
     332           0 :     size_t len = strlen(name);
     333           0 :     for (size_t i = 0; i < len; i++)
     334           0 :         out.printf("%c", tolower(name[i]));
     335           0 : }
     336             : 
     337             : void
     338           0 : LNode::printName(GenericPrinter& out)
     339             : {
     340           0 :     printName(out, op());
     341           0 : }
     342             : 
     343             : bool
     344        2183 : LAllocation::aliases(const LAllocation& other) const
     345             : {
     346        2183 :     if (isFloatReg() && other.isFloatReg())
     347           0 :         return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
     348        2183 :     return *this == other;
     349             : }
     350             : 
     351             : static const char*
     352           0 : typeName(LDefinition::Type type)
     353             : {
     354           0 :     switch (type) {
     355           0 :       case LDefinition::GENERAL: return "g";
     356           0 :       case LDefinition::INT32: return "i";
     357           0 :       case LDefinition::OBJECT: return "o";
     358           0 :       case LDefinition::SLOTS: return "s";
     359           0 :       case LDefinition::FLOAT32: return "f";
     360           0 :       case LDefinition::DOUBLE: return "d";
     361           0 :       case LDefinition::SIMD128INT: return "simd128int";
     362           0 :       case LDefinition::SIMD128FLOAT: return "simd128float";
     363           0 :       case LDefinition::SINCOS: return "sincos";
     364             : #ifdef JS_NUNBOX32
     365             :       case LDefinition::TYPE: return "t";
     366             :       case LDefinition::PAYLOAD: return "p";
     367             : #else
     368           0 :       case LDefinition::BOX: return "x";
     369             : #endif
     370             :     }
     371           0 :     MOZ_CRASH("Invalid type");
     372             : }
     373             : 
     374             : UniqueChars
     375           0 : LDefinition::toString() const
     376             : {
     377           0 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     378             : 
     379           0 :     UniqueChars buf;
     380           0 :     if (isBogusTemp()) {
     381           0 :         buf = JS_smprintf("bogus");
     382             :     } else {
     383           0 :         buf = JS_smprintf("v%u<%s>", virtualRegister(), typeName(type()));
     384           0 :         if (buf) {
     385           0 :             if (policy() == LDefinition::FIXED)
     386           0 :                 buf = JS_sprintf_append(Move(buf), ":%s", output()->toString().get());
     387           0 :             else if (policy() == LDefinition::MUST_REUSE_INPUT)
     388           0 :                 buf = JS_sprintf_append(Move(buf), ":tied(%u)", getReusedInput());
     389             :         }
     390             :     }
     391             : 
     392           0 :     if (!buf)
     393           0 :         oomUnsafe.crash("LDefinition::toString()");
     394             : 
     395           0 :     return buf;
     396             : }
     397             : 
     398             : static UniqueChars
     399           0 : PrintUse(const LUse* use)
     400             : {
     401           0 :     switch (use->policy()) {
     402             :       case LUse::REGISTER:
     403           0 :         return JS_smprintf("v%d:r", use->virtualRegister());
     404             :       case LUse::FIXED:
     405             :         return JS_smprintf("v%d:%s", use->virtualRegister(),
     406           0 :                            AnyRegister::FromCode(use->registerCode()).name());
     407             :       case LUse::ANY:
     408           0 :         return JS_smprintf("v%d:r?", use->virtualRegister());
     409             :       case LUse::KEEPALIVE:
     410           0 :         return JS_smprintf("v%d:*", use->virtualRegister());
     411             :       case LUse::RECOVERED_INPUT:
     412           0 :         return JS_smprintf("v%d:**", use->virtualRegister());
     413             :       default:
     414           0 :         MOZ_CRASH("invalid use policy");
     415             :     }
     416             : }
     417             : 
     418             : UniqueChars
     419         247 : LAllocation::toString() const
     420             : {
     421         494 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     422             : 
     423         247 :     UniqueChars buf;
     424         247 :     if (isBogus()) {
     425           0 :         buf = JS_smprintf("bogus");
     426             :     } else {
     427         247 :         switch (kind()) {
     428             :           case LAllocation::CONSTANT_VALUE:
     429             :           case LAllocation::CONSTANT_INDEX:
     430           0 :             buf = JS_smprintf("c");
     431           0 :             break;
     432             :           case LAllocation::GPR:
     433         190 :             buf = JS_smprintf("%s", toGeneralReg()->reg().name());
     434         190 :             break;
     435             :           case LAllocation::FPU:
     436           0 :             buf = JS_smprintf("%s", toFloatReg()->reg().name());
     437           0 :             break;
     438             :           case LAllocation::STACK_SLOT:
     439           0 :             buf = JS_smprintf("stack:%d", toStackSlot()->slot());
     440           0 :             break;
     441             :           case LAllocation::ARGUMENT_SLOT:
     442          57 :             buf = JS_smprintf("arg:%d", toArgument()->index());
     443          57 :             break;
     444             :           case LAllocation::USE:
     445           0 :             buf = PrintUse(toUse());
     446           0 :             break;
     447             :           default:
     448           0 :             MOZ_CRASH("what?");
     449             :         }
     450             :     }
     451             : 
     452         247 :     if (!buf)
     453           0 :         oomUnsafe.crash("LAllocation::toString()");
     454             : 
     455         494 :     return buf;
     456             : }
     457             : 
     458             : void
     459           0 : LAllocation::dump() const
     460             : {
     461           0 :     fprintf(stderr, "%s\n", toString().get());
     462           0 : }
     463             : 
     464             : void
     465           0 : LDefinition::dump() const
     466             : {
     467           0 :     fprintf(stderr, "%s\n", toString().get());
     468           0 : }
     469             : 
     470             : void
     471           0 : LNode::printOperands(GenericPrinter& out)
     472             : {
     473           0 :     for (size_t i = 0, e = numOperands(); i < e; i++) {
     474           0 :         out.printf(" (%s)", getOperand(i)->toString().get());
     475           0 :         if (i != numOperands() - 1)
     476           0 :             out.printf(",");
     477             :     }
     478           0 : }
     479             : 
     480             : void
     481         326 : LInstruction::assignSnapshot(LSnapshot* snapshot)
     482             : {
     483         326 :     MOZ_ASSERT(!snapshot_);
     484         326 :     snapshot_ = snapshot;
     485             : 
     486             : #ifdef JS_JITSPEW
     487         326 :     if (JitSpewEnabled(JitSpew_IonSnapshots)) {
     488           0 :         JitSpewHeader(JitSpew_IonSnapshots);
     489           0 :         Fprinter& out = JitSpewPrinter();
     490           0 :         out.printf("Assigning snapshot %p to instruction %p (",
     491           0 :                    (void*)snapshot, (void*)this);
     492           0 :         printName(out);
     493           0 :         out.printf(")\n");
     494             :     }
     495             : #endif
     496         326 : }
     497             : 
     498             : void
     499           0 : LNode::dump(GenericPrinter& out)
     500             : {
     501           0 :     if (numDefs() != 0) {
     502           0 :         out.printf("{");
     503           0 :         for (size_t i = 0; i < numDefs(); i++) {
     504           0 :             out.printf("%s", getDef(i)->toString().get());
     505           0 :             if (i != numDefs() - 1)
     506           0 :                 out.printf(", ");
     507             :         }
     508           0 :         out.printf("} <- ");
     509             :     }
     510             : 
     511           0 :     printName(out);
     512           0 :     printOperands(out);
     513             : 
     514           0 :     if (numTemps()) {
     515           0 :         out.printf(" t=(");
     516           0 :         for (size_t i = 0; i < numTemps(); i++) {
     517           0 :             out.printf("%s", getTemp(i)->toString().get());
     518           0 :             if (i != numTemps() - 1)
     519           0 :                 out.printf(", ");
     520             :         }
     521           0 :         out.printf(")");
     522             :     }
     523             : 
     524           0 :     if (numSuccessors()) {
     525           0 :         out.printf(" s=(");
     526           0 :         for (size_t i = 0; i < numSuccessors(); i++) {
     527           0 :             out.printf("block%u", getSuccessor(i)->id());
     528           0 :             if (i != numSuccessors() - 1)
     529           0 :                 out.printf(", ");
     530             :         }
     531           0 :         out.printf(")");
     532             :     }
     533           0 : }
     534             : 
     535             : void
     536           0 : LNode::dump()
     537             : {
     538           0 :     Fprinter out(stderr);
     539           0 :     dump(out);
     540           0 :     out.printf("\n");
     541           0 :     out.finish();
     542           0 : }
     543             : 
     544             : void
     545         134 : LInstruction::initSafepoint(TempAllocator& alloc)
     546             : {
     547         134 :     MOZ_ASSERT(!safepoint_);
     548         134 :     safepoint_ = new(alloc) LSafepoint(alloc);
     549         134 :     MOZ_ASSERT(safepoint_);
     550         134 : }
     551             : 
     552             : bool
     553         554 : LMoveGroup::add(LAllocation from, LAllocation to, LDefinition::Type type)
     554             : {
     555             : #ifdef DEBUG
     556         554 :     MOZ_ASSERT(from != to);
     557         855 :     for (size_t i = 0; i < moves_.length(); i++)
     558         301 :         MOZ_ASSERT(to != moves_[i].to());
     559             : 
     560             :     // Check that SIMD moves are aligned according to ABI requirements.
     561         554 :     if (LDefinition(type).isSimdType()) {
     562           0 :         MOZ_ASSERT(from.isMemory() || from.isFloatReg());
     563           0 :         if (from.isMemory()) {
     564           0 :             if (from.isArgument())
     565           0 :                 MOZ_ASSERT(from.toArgument()->index() % SimdMemoryAlignment == 0);
     566             :             else
     567           0 :                 MOZ_ASSERT(from.toStackSlot()->slot() % SimdMemoryAlignment == 0);
     568             :         }
     569           0 :         MOZ_ASSERT(to.isMemory() || to.isFloatReg());
     570           0 :         if (to.isMemory()) {
     571           0 :             if (to.isArgument())
     572           0 :                 MOZ_ASSERT(to.toArgument()->index() % SimdMemoryAlignment == 0);
     573             :             else
     574           0 :                 MOZ_ASSERT(to.toStackSlot()->slot() % SimdMemoryAlignment == 0);
     575             :         }
     576             :     }
     577             : #endif
     578         554 :     return moves_.append(LMove(from, to, type));
     579             : }
     580             : 
     581             : bool
     582           4 : LMoveGroup::addAfter(LAllocation from, LAllocation to, LDefinition::Type type)
     583             : {
     584             :     // Transform the operands to this move so that performing the result
     585             :     // simultaneously with existing moves in the group will have the same
     586             :     // effect as if the original move took place after the existing moves.
     587             : 
     588           4 :     for (size_t i = 0; i < moves_.length(); i++) {
     589           0 :         if (moves_[i].to() == from) {
     590           0 :             from = moves_[i].from();
     591           0 :             break;
     592             :         }
     593             :     }
     594             : 
     595           4 :     if (from == to)
     596           0 :         return true;
     597             : 
     598           4 :     for (size_t i = 0; i < moves_.length(); i++) {
     599           0 :         if (to == moves_[i].to()) {
     600           0 :             moves_[i] = LMove(from, to, type);
     601           0 :             return true;
     602             :         }
     603             :     }
     604             : 
     605           4 :     return add(from, to, type);
     606             : }
     607             : 
     608             : void
     609           0 : LMoveGroup::printOperands(GenericPrinter& out)
     610             : {
     611           0 :     for (size_t i = 0; i < numMoves(); i++) {
     612           0 :         const LMove& move = getMove(i);
     613           0 :         out.printf(" [%s -> %s", move.from().toString().get(), move.to().toString().get());
     614             : #ifdef DEBUG
     615           0 :         out.printf(", %s", typeName(move.type()));
     616             : #endif
     617           0 :         out.printf("]");
     618           0 :         if (i != numMoves() - 1)
     619           0 :             out.printf(",");
     620             :     }
     621           0 : }

Generated by: LCOV version 1.13