Commit da22163d authored by titzer's avatar titzer Committed by Commit bot

[turbofan] Implement jump threading after register allocation.

R=dcarney@chromium.org
BUG=

Review URL: https://codereview.chromium.org/754843002

Cr-Commit-Position: refs/heads/master@{#25521}
parent a498e93b
......@@ -533,6 +533,8 @@ source_set("v8_base") {
"src/compiler/js-operator.h",
"src/compiler/js-typed-lowering.cc",
"src/compiler/js-typed-lowering.h",
"src/compiler/jump-threading.cc",
"src/compiler/jump-threading.h",
"src/compiler/linkage-impl.h",
"src/compiler/linkage.cc",
"src/compiler/linkage.h",
......
......@@ -120,6 +120,16 @@ bool ParallelMove::IsRedundant() const {
}
bool GapInstruction::IsRedundant() const {
for (int i = GapInstruction::FIRST_INNER_POSITION;
i <= GapInstruction::LAST_INNER_POSITION; i++) {
if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant())
return false;
}
return true;
}
std::ostream& operator<<(std::ostream& os,
const PrintableParallelMove& printable) {
const ParallelMove& pm = *printable.parallel_move_;
......
......@@ -441,6 +441,10 @@ class Instruction : public ZoneObject {
DCHECK(i < InputCount());
return operands_[OutputCount() + i];
}
void SetInputAt(size_t i, InstructionOperand* operand) {
DCHECK(i < InputCount());
operands_[OutputCount() + i] = operand;
}
size_t TempCount() const { return TempCountField::decode(bit_field_); }
InstructionOperand* TempAt(size_t i) const {
......@@ -520,6 +524,17 @@ class Instruction : public ZoneObject {
void operator delete(void* pointer, void* location) { UNREACHABLE(); }
void OverwriteWithNop() {
opcode_ = ArchOpcodeField::encode(kArchNop);
bit_field_ = 0;
pointer_map_ = NULL;
}
bool IsNop() const {
return arch_opcode() == kArchNop && InputCount() == 0 &&
OutputCount() == 0 && TempCount() == 0;
}
protected:
explicit Instruction(InstructionCode opcode)
: opcode_(opcode),
......@@ -599,6 +614,8 @@ class GapInstruction : public Instruction {
return parallel_moves_[pos];
}
bool IsRedundant() const;
static GapInstruction* New(Zone* zone) {
void* buffer = zone->New(sizeof(GapInstruction));
return new (buffer) GapInstruction(kGapInstruction);
......@@ -893,12 +910,16 @@ class InstructionBlock FINAL : public ZoneObject {
const PhiInstructions& phis() const { return phis_; }
void AddPhi(PhiInstruction* phi) { phis_.push_back(phi); }
void set_ao_number(BasicBlock::RpoNumber ao_number) {
ao_number_ = ao_number;
}
private:
Successors successors_;
Predecessors predecessors_;
PhiInstructions phis_;
const BasicBlock::Id id_;
const BasicBlock::RpoNumber ao_number_; // Assembly order number.
BasicBlock::RpoNumber ao_number_; // Assembly order number.
const BasicBlock::RpoNumber rpo_number_;
const BasicBlock::RpoNumber loop_header_;
const BasicBlock::RpoNumber loop_end_;
......@@ -991,6 +1012,8 @@ class InstructionSequence FINAL : public ZoneObject {
void EndBlock(BasicBlock::RpoNumber rpo);
int AddConstant(int virtual_register, Constant constant) {
// TODO(titzer): allow RPO numbers as constants?
DCHECK(constant.type() != Constant::kRpoNumber);
DCHECK(virtual_register >= 0 && virtual_register < next_virtual_register_);
DCHECK(constants_.find(virtual_register) == constants_.end());
constants_.insert(std::make_pair(virtual_register, constant));
......@@ -1003,8 +1026,8 @@ class InstructionSequence FINAL : public ZoneObject {
return it->second;
}
typedef ConstantDeque Immediates;
const Immediates& immediates() const { return immediates_; }
typedef ZoneVector<Constant> Immediates;
Immediates& immediates() { return immediates_; }
int AddImmediate(Constant constant) {
int index = static_cast<int>(immediates_.size());
......@@ -1031,6 +1054,13 @@ class InstructionSequence FINAL : public ZoneObject {
FrameStateDescriptor* GetFrameStateDescriptor(StateId deoptimization_id);
int GetFrameStateDescriptorCount();
BasicBlock::RpoNumber InputRpo(Instruction* instr, size_t index) {
InstructionOperand* operand = instr->InputAt(index);
Constant constant = operand->IsImmediate() ? GetImmediate(operand->index())
: GetConstant(operand->index());
return constant.ToRpoNumber();
}
private:
friend std::ostream& operator<<(std::ostream& os,
const PrintableInstructionSequence& code);
......@@ -1041,7 +1071,7 @@ class InstructionSequence FINAL : public ZoneObject {
InstructionBlocks* const instruction_blocks_;
IntVector block_starts_;
ConstantMap constants_;
ConstantDeque immediates_;
Immediates immediates_;
InstructionDeque instructions_;
int next_virtual_register_;
PointerMapDeque pointer_maps_;
......
// Copyright 2014 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/compiler/jump-threading.h"
#include "src/compiler/code-generator-impl.h"
namespace v8 {
namespace internal {
namespace compiler {
typedef BasicBlock::RpoNumber RpoNumber;
#define TRACE(x) \
if (FLAG_trace_turbo_jt) PrintF x
struct JumpThreadingState {
bool forwarded;
ZoneVector<RpoNumber>& result;
ZoneStack<RpoNumber>& stack;
void Clear(size_t count) { result.assign(count, unvisited()); }
void PushIfUnvisited(RpoNumber num) {
if (result[num.ToInt()] == unvisited()) {
stack.push(num);
result[num.ToInt()] = onstack();
}
}
void Forward(RpoNumber to) {
RpoNumber from = stack.top();
RpoNumber to_to = result[to.ToInt()];
bool pop = true;
if (to == from) {
TRACE((" xx %d\n", from.ToInt()));
result[from.ToInt()] = from;
} else if (to_to == unvisited()) {
TRACE((" fw %d -> %d (recurse)\n", from.ToInt(), to.ToInt()));
stack.push(to);
result[to.ToInt()] = onstack();
pop = false; // recurse.
} else if (to_to == onstack()) {
TRACE((" fw %d -> %d (cycle)\n", from.ToInt(), to.ToInt()));
result[from.ToInt()] = to; // break the cycle.
forwarded = true;
} else {
TRACE((" fw %d -> %d (forward)\n", from.ToInt(), to.ToInt()));
result[from.ToInt()] = to_to; // forward the block.
forwarded = true;
}
if (pop) stack.pop();
}
RpoNumber unvisited() { return RpoNumber::FromInt(-1); }
RpoNumber onstack() { return RpoNumber::FromInt(-2); }
};
bool JumpThreading::ComputeForwarding(Zone* local_zone,
ZoneVector<RpoNumber>& result,
InstructionSequence* code) {
ZoneStack<RpoNumber> stack(local_zone);
JumpThreadingState state = {false, result, stack};
state.Clear(code->InstructionBlockCount());
// Iterate over the blocks forward, pushing the blocks onto the stack.
for (auto const block : code->instruction_blocks()) {
RpoNumber current = block->rpo_number();
state.PushIfUnvisited(current);
// Process the stack, which implements DFS through empty blocks.
while (!state.stack.empty()) {
InstructionBlock* block = code->InstructionBlockAt(state.stack.top());
// Process the instructions in a block up to a non-empty instruction.
TRACE(("jt [%d] B%d RPO%d\n", static_cast<int>(stack.size()),
block->id().ToInt(), block->rpo_number().ToInt()));
bool fallthru = true;
RpoNumber fw = block->rpo_number();
for (int i = block->code_start(); i < block->code_end(); ++i) {
Instruction* instr = code->InstructionAt(i);
if (instr->IsGapMoves() && GapInstruction::cast(instr)->IsRedundant()) {
// skip redundant gap moves.
TRACE((" nop gap\n"));
continue;
} else if (instr->IsSourcePosition()) {
// skip source positions.
TRACE((" src pos\n"));
continue;
} else if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
// can't skip instructions with flags continuations.
TRACE((" flags\n"));
fallthru = false;
} else if (instr->IsNop()) {
// skip nops.
TRACE((" nop\n"));
continue;
} else if (instr->arch_opcode() == kArchJmp) {
// try to forward the jump instruction.
TRACE((" jmp\n"));
fw = code->InputRpo(instr, 0);
fallthru = false;
} else {
// can't skip other instructions.
TRACE((" other\n"));
fallthru = false;
}
break;
}
if (fallthru) {
int next = 1 + block->rpo_number().ToInt();
if (next < code->InstructionBlockCount()) fw = RpoNumber::FromInt(next);
}
state.Forward(fw);
}
}
#if DEBUG
for (RpoNumber num : result) DCHECK(num.IsValid());
#endif
for (int i = 0; i < static_cast<int>(result.size()); i++) {
TRACE(("RPO%d B%d ", i,
code->InstructionBlockAt(RpoNumber::FromInt(i))->id().ToInt()));
int to = result[i].ToInt();
if (i != to) {
TRACE(("-> B%d\n",
code->InstructionBlockAt(RpoNumber::FromInt(to))->id().ToInt()));
} else {
TRACE(("\n"));
}
}
return state.forwarded;
}
void JumpThreading::ApplyForwarding(ZoneVector<RpoNumber>& result,
InstructionSequence* code) {
if (!FLAG_turbo_jt) return;
Zone local_zone(code->zone()->isolate());
ZoneVector<bool> skip(static_cast<int>(result.size()), false, &local_zone);
// Skip empty blocks when the previous block doesn't fall through.
bool prev_fallthru = true;
for (auto const block : code->instruction_blocks()) {
int block_num = block->rpo_number().ToInt();
skip[block_num] = !prev_fallthru && result[block_num].ToInt() != block_num;
bool fallthru = true;
for (int i = block->code_start(); i < block->code_end(); ++i) {
Instruction* instr = code->InstructionAt(i);
if (FlagsModeField::decode(instr->opcode()) == kFlags_branch) {
fallthru = false; // branches don't fall through to the next block.
} else if (instr->arch_opcode() == kArchJmp) {
if (skip[block_num]) {
// Overwrite a redundant jump with a nop.
TRACE(("jt-fw nop @%d\n", i));
instr->OverwriteWithNop();
}
fallthru = false; // jumps don't fall through to the next block.
}
}
prev_fallthru = fallthru;
}
// Patch RPO immediates.
InstructionSequence::Immediates& immediates = code->immediates();
for (size_t i = 0; i < immediates.size(); i++) {
Constant constant = immediates[i];
if (constant.type() == Constant::kRpoNumber) {
RpoNumber rpo = constant.ToRpoNumber();
RpoNumber fw = result[rpo.ToInt()];
if (!(fw == rpo)) immediates[i] = Constant(fw);
}
}
// Recompute assembly order numbers.
int ao = 0;
for (auto const block : code->instruction_blocks()) {
if (!block->IsDeferred()) {
block->set_ao_number(RpoNumber::FromInt(ao));
if (!skip[block->rpo_number().ToInt()]) ao++;
}
}
for (auto const block : code->instruction_blocks()) {
if (block->IsDeferred()) {
block->set_ao_number(RpoNumber::FromInt(ao));
if (!skip[block->rpo_number().ToInt()]) ao++;
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2014 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.
#ifndef V8_COMPILER_JUMP_THREADING_H_
#define V8_COMPILER_JUMP_THREADING_H_
#include "src/compiler/instruction.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forwards jumps to empty basic blocks that end with a second jump to the
// destination of the second jump, transitively.
class JumpThreading {
public:
// Compute the forwarding map of basic blocks to their ultimate destination.
// Returns {true} if there is at least one block that is forwarded.
static bool ComputeForwarding(Zone* local_zone,
ZoneVector<BasicBlock::RpoNumber>& result,
InstructionSequence* code);
// Rewrite the instructions to forward jumps and branches.
// May also negate some branches.
static void ApplyForwarding(ZoneVector<BasicBlock::RpoNumber>& forwarding,
InstructionSequence* code);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JUMP_THREADING_H
......@@ -21,6 +21,7 @@
#include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/jump-threading.h"
#include "src/compiler/machine-operator-reducer.h"
#include "src/compiler/pipeline-statistics.h"
#include "src/compiler/register-allocator.h"
......@@ -578,6 +579,18 @@ struct ResolveControlFlowPhase {
};
struct JumpThreadingPhase {
static const char* phase_name() { return "jump threading"; }
void Run(PipelineData* data, Zone* temp_zone) {
ZoneVector<BasicBlock::RpoNumber> result(temp_zone);
if (JumpThreading::ComputeForwarding(temp_zone, result, data->sequence())) {
JumpThreading::ApplyForwarding(result, data->sequence());
}
}
};
struct GenerateCodePhase {
static const char* phase_name() { return "generate code"; }
......@@ -902,7 +915,12 @@ void Pipeline::GenerateCode(Linkage* linkage) {
BeginPhaseKind("code generation");
// Generate native sequence.
// Optimimize jumps.
if (FLAG_turbo_jt) {
Run<JumpThreadingPhase>();
}
// Generate final machine code.
Run<GenerateCodePhase>(linkage);
if (profiler_data != NULL) {
......
......@@ -61,7 +61,7 @@ class BasicBlock FINAL : public ZoneObject {
DCHECK(IsValid());
return static_cast<size_t>(index_);
}
bool IsValid() const { return index_ != kInvalidRpoNumber; }
bool IsValid() const { return index_ >= 0; }
static RpoNumber FromInt(int index) { return RpoNumber(index); }
static RpoNumber Invalid() { return RpoNumber(kInvalidRpoNumber); }
......
......@@ -369,6 +369,7 @@ DEFINE_STRING(trace_turbo_cfg_file, NULL,
DEFINE_BOOL(trace_turbo_types, true, "trace TurboFan's types")
DEFINE_BOOL(trace_turbo_scheduler, false, "trace TurboFan's scheduler")
DEFINE_BOOL(trace_turbo_reduction, false, "trace TurboFan's various reducers")
DEFINE_BOOL(trace_turbo_jt, false, "trace TurboFan's jump threading")
DEFINE_BOOL(turbo_asm, false, "enable TurboFan for asm.js code")
DEFINE_BOOL(turbo_verify, false, "verify TurboFan graphs at each phase")
DEFINE_BOOL(turbo_stats, false, "print TurboFan statistics")
......@@ -391,6 +392,7 @@ DEFINE_BOOL(turbo_reuse_spill_slots, false, "reuse spill slots in TurboFan")
// TODO(dcarney): this is just for experimentation, remove when default.
DEFINE_BOOL(turbo_delay_ssa_decon, false,
"delay ssa deconstruction in TurboFan register allocator")
DEFINE_BOOL(turbo_jt, true, "enable jump threading")
DEFINE_INT(typed_array_max_size_in_heap, 64,
"threshold for in-heap typed array")
......
......@@ -65,6 +65,7 @@
'compiler/test-js-context-specialization.cc',
'compiler/test-js-constant-cache.cc',
'compiler/test-js-typed-lowering.cc',
'compiler/test-jump-threading.cc',
'compiler/test-linkage.cc',
'compiler/test-loop-assignment-analysis.cc',
'compiler/test-machine-operator-reducer.cc',
......
This diff is collapsed.
......@@ -465,6 +465,8 @@
'../../src/compiler/js-operator.h',
'../../src/compiler/js-typed-lowering.cc',
'../../src/compiler/js-typed-lowering.h',
'../../src/compiler/jump-threading.cc',
'../../src/compiler/jump-threading.h',
'../../src/compiler/linkage-impl.h',
'../../src/compiler/linkage.cc',
'../../src/compiler/linkage.h',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment