// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/crankshaft/hydrogen-removable-simulates.h"
#include "src/crankshaft/hydrogen-flow-engine.h"
#include "src/crankshaft/hydrogen-instructions.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
class State : public ZoneObject {
public:
explicit State(Zone* zone)
: zone_(zone), mergelist_(2, zone), first_(true), mode_(NORMAL) { }
State* Process(HInstruction* instr, Zone* zone) {
if (FLAG_trace_removable_simulates) {
PrintF("[%s with state %p in B%d: #%d %s]\n",
mode_ == NORMAL ? "processing" : "collecting",
reinterpret_cast<void*>(this), instr->block()->block_id(),
instr->id(), instr->Mnemonic());
}
// Forward-merge "trains" of simulates after an instruction with observable
// side effects to keep live ranges short.
if (mode_ == COLLECT_CONSECUTIVE_SIMULATES) {
if (instr->IsSimulate()) {
HSimulate* current_simulate = HSimulate::cast(instr);
if (current_simulate->is_candidate_for_removal() &&
!current_simulate->ast_id().IsNone()) {
Remember(current_simulate);
return this;
}
}
FlushSimulates();
mode_ = NORMAL;
}
// Ensure there's a non-foldable HSimulate before an HEnterInlined to avoid
// folding across HEnterInlined.
DCHECK(!(instr->IsEnterInlined() &&
HSimulate::cast(instr->previous())->is_candidate_for_removal()));
if (instr->IsLeaveInlined() || instr->IsReturn()) {
// Never fold simulates from inlined environments into simulates in the
// outer environment. Simply remove all accumulated simulates without
// merging. This is safe because simulates after instructions with side
// effects are never added to the merge list. The same reasoning holds for
// return instructions.
RemoveSimulates();
return this;
}
if (instr->IsControlInstruction()) {
// Merge the accumulated simulates at the end of the block.
FlushSimulates();
return this;
}
if (instr->IsCapturedObject()) {
// Do not merge simulates across captured objects - captured objects
// change environments during environment replay, and such changes
// would not be reflected in the simulate.
FlushSimulates();
return this;
}
// Skip the non-simulates and the first simulate.
if (!instr->IsSimulate()) return this;
if (first_) {
first_ = false;
return this;
}
HSimulate* current_simulate = HSimulate::cast(instr);
if (!current_simulate->is_candidate_for_removal()) {
Remember(current_simulate);
FlushSimulates();
} else if (current_simulate->ast_id().IsNone()) {
DCHECK(current_simulate->next()->IsEnterInlined());
FlushSimulates();
} else if (current_simulate->previous()->HasObservableSideEffects()) {
Remember(current_simulate);
mode_ = COLLECT_CONSECUTIVE_SIMULATES;
} else {
Remember(current_simulate);
}
return this;
}
static State* Merge(State* succ_state,
HBasicBlock* succ_block,
State* pred_state,
HBasicBlock* pred_block,
Zone* zone) {
return (succ_state == NULL)
? pred_state->Copy(succ_block, pred_block, zone)
: succ_state->Merge(succ_block, pred_state, pred_block, zone);
}
static State* Finish(State* state, HBasicBlock* block, Zone* zone) {
if (FLAG_trace_removable_simulates) {
PrintF("[preparing state %p for B%d]\n", reinterpret_cast<void*>(state),
block->block_id());
}
// For our current local analysis, we should not remember simulates across
// block boundaries.
DCHECK(!state->HasRememberedSimulates());
// Nasty heuristic: Never remove the first simulate in a block. This
// just so happens to have a beneficial effect on register allocation.
state->first_ = true;
return state;
}
private:
explicit State(const State& other)
: zone_(other.zone_),
mergelist_(other.mergelist_, other.zone_),
first_(other.first_),
mode_(other.mode_) { }
enum Mode { NORMAL, COLLECT_CONSECUTIVE_SIMULATES };
bool HasRememberedSimulates() const { return !mergelist_.is_empty(); }
void Remember(HSimulate* sim) {
mergelist_.Add(sim, zone_);
}
void FlushSimulates() {
if (HasRememberedSimulates()) {
mergelist_.RemoveLast()->MergeWith(&mergelist_);
}
}
void RemoveSimulates() {
while (HasRememberedSimulates()) {
mergelist_.RemoveLast()->DeleteAndReplaceWith(NULL);
}
}
State* Copy(HBasicBlock* succ_block, HBasicBlock* pred_block, Zone* zone) {
State* copy = new(zone) State(*this);
if (FLAG_trace_removable_simulates) {
PrintF("[copy state %p from B%d to new state %p for B%d]\n",
reinterpret_cast<void*>(this), pred_block->block_id(),
reinterpret_cast<void*>(copy), succ_block->block_id());
}
return copy;
}
State* Merge(HBasicBlock* succ_block,
State* pred_state,
HBasicBlock* pred_block,
Zone* zone) {
// For our current local analysis, we should not remember simulates across
// block boundaries.
DCHECK(!pred_state->HasRememberedSimulates());
DCHECK(!HasRememberedSimulates());
if (FLAG_trace_removable_simulates) {
PrintF("[merge state %p from B%d into %p for B%d]\n",
reinterpret_cast<void*>(pred_state), pred_block->block_id(),
reinterpret_cast<void*>(this), succ_block->block_id());
}
return this;
}
Zone* zone_;
ZoneList<HSimulate*> mergelist_;
bool first_;
Mode mode_;
};
// We don't use effects here.
class Effects : public ZoneObject {
public:
explicit Effects(Zone* zone) { }
bool Disabled() { return true; }
void Process(HInstruction* instr, Zone* zone) { }
void Apply(State* state) { }
void Union(Effects* that, Zone* zone) { }
};
void HMergeRemovableSimulatesPhase::Run() {
HFlowEngine<State, Effects> engine(graph(), zone());
State* state = new(zone()) State(zone());
engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), state);
}
} // namespace internal
} // namespace v8