Revert of [turbofan] Osr value typing + dynamic type checks on entry. (patchset #5 id:80001 of https://codereview.chromium.org/2384113002/ )
Reason for revert: Tanks the world. Original issue's description: > [turbofan] Osr value typing + dynamic type checks on entry. > > This introduces a new OsrGuard node that is inserted during graph building > to guard the inferred type of the OSR value. > > The type of the OSR value is inferred by running the typer before OSR > deconstruction, and then taking the type from the phi that takes the > OSR value. After the deconstruction, we throw the types away. > > At the moment we only support the SignedSmall OSR type and we always > pick the tagged representation. Later, we might want to support more > types (such as Number) and pick better representations (int32/float64). > > This CL also removes the OSR deconstruction tests because they build > unrealistic graph (no effect chain, no loop termination). I considered > adding the effect chains to the tests, but this would make the tests > even more brittle. > > Committed: https://crrev.com/1f5dc90a900d222da44bee3eff171a2ba1e3c076 > Cr-Commit-Position: refs/heads/master@{#39971} TBR=bmeurer@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2395783002 Cr-Commit-Position: refs/heads/master@{#39985}
This commit is contained in:
parent
4eaccc7a45
commit
ff81734cb9
@ -788,10 +788,8 @@ AstGraphBuilder::Environment::CopyAsUnreachable() {
|
||||
}
|
||||
|
||||
AstGraphBuilder::Environment* AstGraphBuilder::Environment::CopyForOsrEntry() {
|
||||
LivenessAnalyzerBlock* copy_block =
|
||||
liveness_block() == nullptr ? nullptr
|
||||
: builder_->liveness_analyzer()->NewBlock();
|
||||
return new (zone()) Environment(this, copy_block);
|
||||
return new (zone())
|
||||
Environment(this, builder_->liveness_analyzer()->NewBlock());
|
||||
}
|
||||
|
||||
AstGraphBuilder::Environment*
|
||||
@ -4204,47 +4202,27 @@ void AstGraphBuilder::Environment::PrepareForOsrEntry() {
|
||||
graph->start(), graph->start());
|
||||
UpdateControlDependency(osr_loop_entry);
|
||||
UpdateEffectDependency(osr_loop_entry);
|
||||
|
||||
// Set OSR values.
|
||||
for (int i = 0; i < size; ++i) {
|
||||
values()->at(i) =
|
||||
graph->NewNode(builder_->common()->OsrValue(i), osr_loop_entry);
|
||||
}
|
||||
|
||||
// Set the innermost context.
|
||||
const Operator* op_inner =
|
||||
builder_->common()->OsrValue(Linkage::kOsrContextSpillSlotIndex);
|
||||
contexts()->back() = graph->NewNode(op_inner, osr_loop_entry);
|
||||
|
||||
// Create a checkpoint.
|
||||
Node* frame_state = Checkpoint(builder_->info()->osr_ast_id());
|
||||
Node* checkpoint = graph->NewNode(common()->Checkpoint(), frame_state,
|
||||
osr_loop_entry, osr_loop_entry);
|
||||
UpdateEffectDependency(checkpoint);
|
||||
|
||||
// Create the OSR guard nodes.
|
||||
const Operator* guard_op =
|
||||
builder_->common()->OsrGuard(OsrGuardType::kUninitialized);
|
||||
Node* effect = checkpoint;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
values()->at(i) = effect =
|
||||
graph->NewNode(guard_op, values()->at(i), effect, osr_loop_entry);
|
||||
}
|
||||
contexts()->back() = effect =
|
||||
graph->NewNode(guard_op, contexts()->back(), effect, osr_loop_entry);
|
||||
|
||||
// Set the contexts.
|
||||
// The innermost context is the OSR value, and the outer contexts are
|
||||
// reconstructed by dynamically walking up the context chain.
|
||||
const Operator* load_op =
|
||||
Node* osr_context = nullptr;
|
||||
const Operator* op =
|
||||
builder_->javascript()->LoadContext(0, Context::PREVIOUS_INDEX, true);
|
||||
Node* osr_context = effect = contexts()->back();
|
||||
const Operator* op_inner =
|
||||
builder_->common()->OsrValue(Linkage::kOsrContextSpillSlotIndex);
|
||||
int last = static_cast<int>(contexts()->size() - 1);
|
||||
for (int i = last - 1; i >= 0; i--) {
|
||||
osr_context = effect =
|
||||
graph->NewNode(load_op, osr_context, effect, osr_loop_entry);
|
||||
for (int i = last; i >= 0; i--) {
|
||||
osr_context = (i == last) ? graph->NewNode(op_inner, osr_loop_entry)
|
||||
: graph->NewNode(op, osr_context, osr_context,
|
||||
osr_loop_entry);
|
||||
contexts()->at(i) = osr_context;
|
||||
}
|
||||
UpdateEffectDependency(effect);
|
||||
}
|
||||
|
||||
void AstGraphBuilder::Environment::PrepareForLoop(BitVector* assigned) {
|
||||
|
@ -438,24 +438,6 @@ void BytecodeGraphBuilder::Environment::PrepareForOsrEntry() {
|
||||
if (i >= accumulator_base()) idx = Linkage::kOsrAccumulatorRegisterIndex;
|
||||
values()->at(i) = graph()->NewNode(common()->OsrValue(idx), entry);
|
||||
}
|
||||
|
||||
BailoutId loop_id(builder_->bytecode_iterator().current_offset());
|
||||
Node* frame_state =
|
||||
Checkpoint(loop_id, OutputFrameStateCombine::Ignore(), false);
|
||||
Node* checkpoint =
|
||||
graph()->NewNode(common()->Checkpoint(), frame_state, entry, entry);
|
||||
UpdateEffectDependency(checkpoint);
|
||||
|
||||
// Create the OSR guard nodes.
|
||||
const Operator* guard_op = common()->OsrGuard(OsrGuardType::kUninitialized);
|
||||
Node* effect = checkpoint;
|
||||
for (int i = 0; i < size; i++) {
|
||||
values()->at(i) = effect =
|
||||
graph()->NewNode(guard_op, values()->at(i), effect, entry);
|
||||
}
|
||||
Node* context = effect = graph()->NewNode(guard_op, Context(), effect, entry);
|
||||
SetContext(context);
|
||||
UpdateEffectDependency(effect);
|
||||
}
|
||||
|
||||
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
|
||||
|
@ -210,31 +210,6 @@ std::ostream& operator<<(std::ostream& os,
|
||||
return os;
|
||||
}
|
||||
|
||||
int OsrValueIndexOf(Operator const* op) {
|
||||
DCHECK_EQ(IrOpcode::kOsrValue, op->opcode());
|
||||
return OpParameter<int>(op);
|
||||
}
|
||||
|
||||
size_t hash_value(OsrGuardType type) { return static_cast<size_t>(type); }
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, OsrGuardType type) {
|
||||
switch (type) {
|
||||
case OsrGuardType::kUninitialized:
|
||||
return os << "Uninitialized";
|
||||
case OsrGuardType::kSignedSmall:
|
||||
return os << "SignedSmall";
|
||||
case OsrGuardType::kAny:
|
||||
return os << "Any";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return os;
|
||||
}
|
||||
|
||||
OsrGuardType OsrGuardTypeOf(Operator const* op) {
|
||||
DCHECK_EQ(IrOpcode::kOsrGuard, op->opcode());
|
||||
return OpParameter<OsrGuardType>(op);
|
||||
}
|
||||
|
||||
#define CACHED_OP_LIST(V) \
|
||||
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
|
||||
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
|
||||
@ -805,6 +780,7 @@ const Operator* CommonOperatorBuilder::Parameter(int index,
|
||||
ParameterInfo(index, debug_name)); // parameter info
|
||||
}
|
||||
|
||||
|
||||
const Operator* CommonOperatorBuilder::OsrValue(int index) {
|
||||
return new (zone()) Operator1<int>( // --
|
||||
IrOpcode::kOsrValue, Operator::kNoProperties, // opcode
|
||||
@ -813,13 +789,6 @@ const Operator* CommonOperatorBuilder::OsrValue(int index) {
|
||||
index); // parameter
|
||||
}
|
||||
|
||||
const Operator* CommonOperatorBuilder::OsrGuard(OsrGuardType type) {
|
||||
return new (zone()) Operator1<OsrGuardType>( // --
|
||||
IrOpcode::kOsrGuard, Operator::kNoThrow, // opcode
|
||||
"OsrGuard", // name
|
||||
1, 1, 1, 1, 1, 0, // counts
|
||||
type); // parameter
|
||||
}
|
||||
|
||||
const Operator* CommonOperatorBuilder::Int32Constant(int32_t value) {
|
||||
return new (zone()) Operator1<int32_t>( // --
|
||||
|
@ -171,13 +171,6 @@ std::ostream& operator<<(std::ostream& os,
|
||||
|
||||
Type* TypeGuardTypeOf(Operator const*) WARN_UNUSED_RESULT;
|
||||
|
||||
int OsrValueIndexOf(Operator const*);
|
||||
|
||||
enum class OsrGuardType { kUninitialized, kSignedSmall, kAny };
|
||||
size_t hash_value(OsrGuardType type);
|
||||
std::ostream& operator<<(std::ostream&, OsrGuardType);
|
||||
OsrGuardType OsrGuardTypeOf(Operator const*);
|
||||
|
||||
// Interface for building common operators that can be used at any level of IR,
|
||||
// including JavaScript, mid-level, and low-level.
|
||||
class CommonOperatorBuilder final : public ZoneObject {
|
||||
@ -209,7 +202,6 @@ class CommonOperatorBuilder final : public ZoneObject {
|
||||
const Operator* OsrNormalEntry();
|
||||
const Operator* OsrLoopEntry();
|
||||
const Operator* OsrValue(int index);
|
||||
const Operator* OsrGuard(OsrGuardType type);
|
||||
|
||||
const Operator* Int32Constant(int32_t);
|
||||
const Operator* Int64Constant(int64_t);
|
||||
|
@ -1733,7 +1733,7 @@ void InstructionSelector::VisitIfException(Node* node) {
|
||||
|
||||
void InstructionSelector::VisitOsrValue(Node* node) {
|
||||
OperandGenerator g(this);
|
||||
int index = OsrValueIndexOf(node->op());
|
||||
int index = OpParameter<int>(node);
|
||||
Emit(kArchNop,
|
||||
g.DefineAsLocation(node, linkage()->GetOsrValueLocation(index)));
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ Reduction JSFrameSpecialization::Reduce(Node* node) {
|
||||
Reduction JSFrameSpecialization::ReduceOsrValue(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kOsrValue, node->opcode());
|
||||
Handle<Object> value;
|
||||
int index = OsrValueIndexOf(node->op());
|
||||
int const index = OpParameter<int>(node);
|
||||
int const parameters_count = frame()->ComputeParametersCount() + 1;
|
||||
if (index == Linkage::kOsrContextSpillSlotIndex) {
|
||||
value = handle(frame()->context(), isolate());
|
||||
|
@ -369,7 +369,7 @@ MaybeHandle<Context> NodeProperties::GetSpecializationNativeContext(
|
||||
return handle(context->native_context());
|
||||
}
|
||||
case IrOpcode::kOsrValue: {
|
||||
int const index = OsrValueIndexOf(node->op());
|
||||
int const index = OpParameter<int>(node);
|
||||
if (index == Linkage::kOsrContextSpillSlotIndex) {
|
||||
return native_context;
|
||||
}
|
||||
|
@ -58,7 +58,6 @@
|
||||
V(Call) \
|
||||
V(Parameter) \
|
||||
V(OsrValue) \
|
||||
V(OsrGuard) \
|
||||
V(LoopExit) \
|
||||
V(LoopExitValue) \
|
||||
V(LoopExitEffect) \
|
||||
|
@ -47,14 +47,13 @@ OsrHelper::OsrHelper(CompilationInfo* info)
|
||||
if (TRACE_COND) PrintF(__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
namespace {
|
||||
|
||||
// Peel outer loops and rewire the graph so that control reduction can
|
||||
// produce a properly formed graph.
|
||||
void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
Zone* tmp_zone, Node* dead, LoopTree* loop_tree,
|
||||
LoopTree::Loop* osr_loop, Node* osr_normal_entry,
|
||||
Node* osr_loop_entry) {
|
||||
static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
Zone* tmp_zone, Node* dead,
|
||||
LoopTree* loop_tree, LoopTree::Loop* osr_loop,
|
||||
Node* osr_normal_entry, Node* osr_loop_entry) {
|
||||
const size_t original_count = graph->NodeCount();
|
||||
AllNodes all(tmp_zone, graph);
|
||||
NodeVector tmp_inputs(tmp_zone);
|
||||
@ -94,8 +93,7 @@ void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
continue;
|
||||
}
|
||||
if (orig->InputCount() == 0 || orig->opcode() == IrOpcode::kParameter ||
|
||||
orig->opcode() == IrOpcode::kOsrValue ||
|
||||
orig->opcode() == IrOpcode::kOsrGuard) {
|
||||
orig->opcode() == IrOpcode::kOsrValue) {
|
||||
// No need to copy leaf nodes or parameters.
|
||||
mapping->at(orig->id()) = orig;
|
||||
continue;
|
||||
@ -257,41 +255,6 @@ void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
}
|
||||
}
|
||||
|
||||
void SetTypeForOsrValue(Node* osr_value, Node* loop,
|
||||
CommonOperatorBuilder* common) {
|
||||
Node* osr_guard = nullptr;
|
||||
for (Node* use : osr_value->uses()) {
|
||||
if (use->opcode() == IrOpcode::kOsrGuard) {
|
||||
DCHECK_EQ(use->InputAt(0), osr_value);
|
||||
osr_guard = use;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OsrGuardType guard_type = OsrGuardType::kAny;
|
||||
// Find the phi that uses the OsrGuard node and get the type from
|
||||
// there. Skip the search if the OsrGuard does not have value use
|
||||
// (i.e., if there is other use beyond the effect use).
|
||||
if (osr_guard->UseCount() > 1) {
|
||||
Type* type = nullptr;
|
||||
for (Node* use : osr_guard->uses()) {
|
||||
if (use->opcode() == IrOpcode::kPhi) {
|
||||
if (NodeProperties::GetControlInput(use) != loop) continue;
|
||||
CHECK_NULL(type);
|
||||
type = NodeProperties::GetType(use);
|
||||
}
|
||||
}
|
||||
CHECK_NOT_NULL(type);
|
||||
|
||||
if (type->Is(Type::SignedSmall())) {
|
||||
guard_type = OsrGuardType::kSignedSmall;
|
||||
}
|
||||
}
|
||||
|
||||
NodeProperties::ChangeOp(osr_guard, common->OsrGuard(guard_type));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
|
||||
Zone* tmp_zone) {
|
||||
@ -320,12 +283,6 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
|
||||
|
||||
CHECK(osr_loop); // Should have found the OSR loop.
|
||||
|
||||
for (Node* use : osr_loop_entry->uses()) {
|
||||
if (use->opcode() == IrOpcode::kOsrValue) {
|
||||
SetTypeForOsrValue(use, osr_loop, common);
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze the graph to determine how deeply nested the OSR loop is.
|
||||
LoopTree* loop_tree = LoopFinder::BuildLoopTree(graph, tmp_zone);
|
||||
|
||||
|
@ -860,20 +860,7 @@ struct TyperPhase {
|
||||
}
|
||||
};
|
||||
|
||||
struct OsrTyperPhase {
|
||||
static const char* phase_name() { return "osr typer"; }
|
||||
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
NodeVector roots(temp_zone);
|
||||
data->jsgraph()->GetCachedNodes(&roots);
|
||||
// Dummy induction variable optimizer: at the moment, we do not try
|
||||
// to compute loop variable bounds on OSR.
|
||||
LoopVariableOptimizer induction_vars(data->jsgraph()->graph(),
|
||||
data->common(), temp_zone);
|
||||
Typer typer(data->isolate(), data->graph());
|
||||
typer.Run(roots, &induction_vars);
|
||||
}
|
||||
};
|
||||
#ifdef DEBUG
|
||||
|
||||
struct UntyperPhase {
|
||||
static const char* phase_name() { return "untyper"; }
|
||||
@ -890,12 +877,6 @@ struct UntyperPhase {
|
||||
}
|
||||
};
|
||||
|
||||
NodeVector roots(temp_zone);
|
||||
data->jsgraph()->GetCachedNodes(&roots);
|
||||
for (Node* node : roots) {
|
||||
NodeProperties::RemoveType(node);
|
||||
}
|
||||
|
||||
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
|
||||
RemoveTypeReducer remove_type_reducer;
|
||||
AddReducer(data, &graph_reducer, &remove_type_reducer);
|
||||
@ -903,15 +884,12 @@ struct UntyperPhase {
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
struct OsrDeconstructionPhase {
|
||||
static const char* phase_name() { return "OSR deconstruction"; }
|
||||
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
GraphTrimmer trimmer(temp_zone, data->graph());
|
||||
NodeVector roots(temp_zone);
|
||||
data->jsgraph()->GetCachedNodes(&roots);
|
||||
trimmer.TrimGraph(roots.begin(), roots.end());
|
||||
|
||||
OsrHelper osr_helper(data->info());
|
||||
osr_helper.Deconstruct(data->jsgraph(), data->common(), temp_zone);
|
||||
}
|
||||
@ -1518,11 +1496,7 @@ bool PipelineImpl::CreateGraph() {
|
||||
|
||||
// Perform OSR deconstruction.
|
||||
if (info()->is_osr()) {
|
||||
Run<OsrTyperPhase>();
|
||||
|
||||
Run<OsrDeconstructionPhase>();
|
||||
|
||||
Run<UntyperPhase>();
|
||||
RunPrintAndVerify("OSR deconstruction", true);
|
||||
}
|
||||
|
||||
|
@ -161,8 +161,7 @@ void ReplaceEffectControlUses(Node* node, Node* effect, Node* control) {
|
||||
} else if (NodeProperties::IsEffectEdge(edge)) {
|
||||
edge.UpdateTo(effect);
|
||||
} else {
|
||||
DCHECK(NodeProperties::IsValueEdge(edge) ||
|
||||
NodeProperties::IsContextEdge(edge));
|
||||
DCHECK(NodeProperties::IsValueEdge(edge));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1267,30 +1266,6 @@ class RepresentationSelector {
|
||||
return;
|
||||
}
|
||||
|
||||
void VisitOsrGuard(Node* node) {
|
||||
VisitInputs(node);
|
||||
|
||||
// Insert a dynamic check for the OSR value type if necessary.
|
||||
switch (OsrGuardTypeOf(node->op())) {
|
||||
case OsrGuardType::kUninitialized:
|
||||
// At this point, we should always have a type for the OsrValue.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
case OsrGuardType::kSignedSmall:
|
||||
if (lower()) {
|
||||
NodeProperties::ChangeOp(node,
|
||||
simplified()->CheckedTaggedToTaggedSigned());
|
||||
}
|
||||
return SetOutput(node, MachineRepresentation::kTaggedSigned);
|
||||
case OsrGuardType::kAny: // Nothing to check.
|
||||
if (lower()) {
|
||||
DeferReplacement(node, node->InputAt(0));
|
||||
}
|
||||
return SetOutput(node, MachineRepresentation::kTagged);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Dispatching routine for visiting the node {node} with the usage {use}.
|
||||
// Depending on the operator, propagate new usage info to the inputs.
|
||||
void VisitNode(Node* node, Truncation truncation,
|
||||
@ -2434,9 +2409,6 @@ class RepresentationSelector {
|
||||
return;
|
||||
}
|
||||
|
||||
case IrOpcode::kOsrGuard:
|
||||
return VisitOsrGuard(node);
|
||||
|
||||
// Operators with all inputs tagged and no or tagged output have uniform
|
||||
// handling.
|
||||
case IrOpcode::kEnd:
|
||||
@ -2455,9 +2427,9 @@ class RepresentationSelector {
|
||||
case IrOpcode::kThrow:
|
||||
case IrOpcode::kBeginRegion:
|
||||
case IrOpcode::kFinishRegion:
|
||||
case IrOpcode::kOsrValue:
|
||||
case IrOpcode::kProjection:
|
||||
case IrOpcode::kObjectState:
|
||||
case IrOpcode::kOsrValue:
|
||||
// All JavaScript operators except JSToNumber have uniform handling.
|
||||
#define OPCODE_CASE(name) case IrOpcode::k##name:
|
||||
JS_SIMPLE_BINOP_LIST(OPCODE_CASE)
|
||||
|
@ -550,19 +550,6 @@ Type* Typer::Visitor::TypeParameter(Node* node) { return Type::Any(); }
|
||||
|
||||
Type* Typer::Visitor::TypeOsrValue(Node* node) { return Type::Any(); }
|
||||
|
||||
Type* Typer::Visitor::TypeOsrGuard(Node* node) {
|
||||
switch (OsrGuardTypeOf(node->op())) {
|
||||
case OsrGuardType::kUninitialized:
|
||||
return Type::None();
|
||||
case OsrGuardType::kSignedSmall:
|
||||
return Type::SignedSmall();
|
||||
case OsrGuardType::kAny:
|
||||
return Type::Any();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Type* Typer::Visitor::TypeRetain(Node* node) {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
|
@ -375,23 +375,6 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
// Type is merged from other values in the graph and could be any.
|
||||
CheckTypeIs(node, Type::Any());
|
||||
break;
|
||||
case IrOpcode::kOsrGuard:
|
||||
// OSR values have a value and a control input.
|
||||
CHECK_EQ(1, value_count);
|
||||
CHECK_EQ(1, effect_count);
|
||||
CHECK_EQ(1, control_count);
|
||||
switch (OsrGuardTypeOf(node->op())) {
|
||||
case OsrGuardType::kUninitialized:
|
||||
CheckTypeIs(node, Type::None());
|
||||
break;
|
||||
case OsrGuardType::kSignedSmall:
|
||||
CheckTypeIs(node, Type::SignedSmall());
|
||||
break;
|
||||
case OsrGuardType::kAny:
|
||||
CheckTypeIs(node, Type::Any());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IrOpcode::kProjection: {
|
||||
// Projection has an input that produces enough values.
|
||||
int index = static_cast<int>(ProjectionIndexOf(node->op()));
|
||||
|
@ -40,6 +40,7 @@ v8_executable("cctest") {
|
||||
"compiler/test-multiple-return.cc",
|
||||
"compiler/test-node.cc",
|
||||
"compiler/test-operator.cc",
|
||||
"compiler/test-osr.cc",
|
||||
"compiler/test-representation-change.cc",
|
||||
"compiler/test-run-bytecode-graph-builder.cc",
|
||||
"compiler/test-run-calls-to-external-references.cc",
|
||||
|
@ -60,6 +60,7 @@
|
||||
'compiler/test-multiple-return.cc',
|
||||
'compiler/test-node.cc',
|
||||
'compiler/test-operator.cc',
|
||||
'compiler/test-osr.cc',
|
||||
'compiler/test-representation-change.cc',
|
||||
'compiler/test-run-bytecode-graph-builder.cc',
|
||||
'compiler/test-run-calls-to-external-references.cc',
|
||||
|
575
test/cctest/compiler/test-osr.cc
Normal file
575
test/cctest/compiler/test-osr.cc
Normal file
@ -0,0 +1,575 @@
|
||||
// Copyright 2015 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/codegen.h"
|
||||
#include "src/compiler/all-nodes.h"
|
||||
#include "src/compiler/common-operator.h"
|
||||
#include "src/compiler/diamond.h"
|
||||
#include "src/compiler/graph.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/js-operator.h"
|
||||
#include "src/compiler/operator.h"
|
||||
#include "src/compiler/osr.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
// TODO(titzer): move this method to a common testing place.
|
||||
|
||||
static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL,
|
||||
Node* i2 = NULL, Node* i3 = NULL) {
|
||||
int count = 4;
|
||||
if (i3 == NULL) count = 3;
|
||||
if (i2 == NULL) count = 2;
|
||||
if (i1 == NULL) count = 1;
|
||||
if (i0 == NULL) count = 0;
|
||||
CHECK_EQ(count, node->InputCount());
|
||||
if (i0 != NULL) CHECK_EQ(i0, node->InputAt(0));
|
||||
if (i1 != NULL) CHECK_EQ(i1, node->InputAt(1));
|
||||
if (i2 != NULL) CHECK_EQ(i2, node->InputAt(2));
|
||||
if (i3 != NULL) CHECK_EQ(i3, node->InputAt(3));
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
|
||||
"Int32LessThan", 2, 0, 0, 1, 0, 0);
|
||||
static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
|
||||
0, 1, 0, 0);
|
||||
|
||||
|
||||
static const int kMaxOsrValues = 10;
|
||||
|
||||
class OsrDeconstructorTester : public HandleAndZoneScope {
|
||||
public:
|
||||
explicit OsrDeconstructorTester(int num_values)
|
||||
: isolate(main_isolate()),
|
||||
common(main_zone()),
|
||||
graph(main_zone()),
|
||||
jsgraph(main_isolate(), &graph, &common, nullptr, nullptr, nullptr),
|
||||
start(graph.NewNode(common.Start(1))),
|
||||
p0(graph.NewNode(common.Parameter(0), start)),
|
||||
end(graph.NewNode(common.End(1), start)),
|
||||
osr_normal_entry(graph.NewNode(common.OsrNormalEntry(), start, start)),
|
||||
osr_loop_entry(graph.NewNode(common.OsrLoopEntry(), start, start)),
|
||||
self(graph.NewNode(common.Int32Constant(0xaabbccdd))) {
|
||||
CHECK(num_values <= kMaxOsrValues);
|
||||
graph.SetStart(start);
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
osr_values[i] = graph.NewNode(common.OsrValue(i), osr_loop_entry);
|
||||
}
|
||||
}
|
||||
|
||||
Isolate* isolate;
|
||||
CommonOperatorBuilder common;
|
||||
Graph graph;
|
||||
JSGraph jsgraph;
|
||||
Node* start;
|
||||
Node* p0;
|
||||
Node* end;
|
||||
Node* osr_normal_entry;
|
||||
Node* osr_loop_entry;
|
||||
Node* self;
|
||||
Node* osr_values[kMaxOsrValues];
|
||||
|
||||
Node* NewOsrPhi(Node* loop, Node* incoming, int osr_value, Node* back1 = NULL,
|
||||
Node* back2 = NULL, Node* back3 = NULL) {
|
||||
int count = 5;
|
||||
if (back3 == NULL) count = 4;
|
||||
if (back2 == NULL) count = 3;
|
||||
if (back1 == NULL) count = 2;
|
||||
CHECK_EQ(loop->InputCount(), count);
|
||||
CHECK_EQ(osr_loop_entry, loop->InputAt(1));
|
||||
|
||||
Node* inputs[6];
|
||||
inputs[0] = incoming;
|
||||
inputs[1] = osr_values[osr_value];
|
||||
if (count > 2) inputs[2] = back1;
|
||||
if (count > 3) inputs[3] = back2;
|
||||
if (count > 4) inputs[4] = back3;
|
||||
inputs[count] = loop;
|
||||
return graph.NewNode(common.Phi(MachineRepresentation::kTagged, count),
|
||||
count + 1, inputs);
|
||||
}
|
||||
|
||||
Node* NewLoop(bool is_osr, int num_backedges, Node* entry = nullptr) {
|
||||
if (entry == nullptr) entry = osr_normal_entry;
|
||||
Node* loop = graph.NewNode(common.Loop(1), entry);
|
||||
if (is_osr) {
|
||||
loop->AppendInput(graph.zone(), osr_loop_entry);
|
||||
}
|
||||
for (int i = 0; i < num_backedges; i++) {
|
||||
loop->AppendInput(graph.zone(), loop);
|
||||
}
|
||||
NodeProperties::ChangeOp(loop, common.Loop(loop->InputCount()));
|
||||
return loop;
|
||||
}
|
||||
|
||||
Node* NewOsrLoop(int num_backedges, Node* entry = NULL) {
|
||||
return NewLoop(true, num_backedges, entry);
|
||||
}
|
||||
|
||||
void DeconstructOsr() {
|
||||
OsrHelper helper(0, 0);
|
||||
helper.Deconstruct(&jsgraph, &common, main_zone());
|
||||
AllNodes nodes(main_zone(), &graph);
|
||||
// Should be edited out.
|
||||
CHECK(!nodes.IsLive(osr_normal_entry));
|
||||
CHECK(!nodes.IsLive(osr_loop_entry));
|
||||
// No dangling nodes should be left over.
|
||||
for (Node* const node : nodes.reachable) {
|
||||
for (Node* const use : node->uses()) {
|
||||
CHECK(std::find(nodes.reachable.begin(), nodes.reachable.end(), use) !=
|
||||
nodes.reachable.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST(Deconstruct_osr0) {
|
||||
OsrDeconstructorTester T(0);
|
||||
|
||||
Node* loop = T.NewOsrLoop(1);
|
||||
|
||||
T.graph.SetEnd(loop);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, loop);
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr1) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
Node* loop = T.NewOsrLoop(1);
|
||||
Node* osr_phi =
|
||||
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
||||
|
||||
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
|
||||
T.graph.SetEnd(ret);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, loop);
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
||||
CheckInputs(ret, osr_phi, T.start, loop);
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_remove_prologue) {
|
||||
OsrDeconstructorTester T(1);
|
||||
Diamond d(&T.graph, &T.common, T.p0);
|
||||
d.Chain(T.osr_normal_entry);
|
||||
|
||||
Node* loop = T.NewOsrLoop(1, d.merge);
|
||||
Node* osr_phi =
|
||||
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
||||
|
||||
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
|
||||
T.graph.SetEnd(ret);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, loop);
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
||||
CheckInputs(ret, osr_phi, T.start, loop);
|
||||
|
||||
// The control before the loop should have been removed.
|
||||
AllNodes nodes(T.main_zone(), &T.graph);
|
||||
CHECK(!nodes.IsLive(d.branch));
|
||||
CHECK(!nodes.IsLive(d.if_true));
|
||||
CHECK(!nodes.IsLive(d.if_false));
|
||||
CHECK(!nodes.IsLive(d.merge));
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_with_body1) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
Node* loop = T.NewOsrLoop(1);
|
||||
|
||||
Node* branch = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
||||
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
|
||||
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
|
||||
loop->ReplaceInput(2, if_true);
|
||||
|
||||
Node* osr_phi =
|
||||
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
||||
|
||||
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false);
|
||||
T.graph.SetEnd(ret);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, if_true);
|
||||
CheckInputs(branch, T.p0, loop);
|
||||
CheckInputs(if_true, branch);
|
||||
CheckInputs(if_false, branch);
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
||||
CheckInputs(ret, osr_phi, T.start, if_false);
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_with_body2) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
Node* loop = T.NewOsrLoop(1);
|
||||
|
||||
// Two chained branches in the the body of the loop.
|
||||
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
||||
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
|
||||
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
|
||||
|
||||
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
|
||||
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
|
||||
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
|
||||
loop->ReplaceInput(2, if_true2);
|
||||
|
||||
Node* osr_phi =
|
||||
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
||||
|
||||
Node* merge = T.graph.NewNode(T.common.Merge(2), if_false1, if_false2);
|
||||
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, merge);
|
||||
T.graph.SetEnd(ret);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, if_true2);
|
||||
CheckInputs(branch1, T.p0, loop);
|
||||
CheckInputs(branch2, T.p0, if_true1);
|
||||
CheckInputs(if_true1, branch1);
|
||||
CheckInputs(if_false1, branch1);
|
||||
CheckInputs(if_true2, branch2);
|
||||
CheckInputs(if_false2, branch2);
|
||||
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
||||
CheckInputs(ret, osr_phi, T.start, merge);
|
||||
CheckInputs(merge, if_false1, if_false2);
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_with_body3) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
Node* loop = T.NewOsrLoop(2);
|
||||
|
||||
// Two branches that create two different backedges.
|
||||
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
||||
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
|
||||
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
|
||||
|
||||
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
|
||||
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
|
||||
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
|
||||
loop->ReplaceInput(2, if_false1);
|
||||
loop->ReplaceInput(3, if_true2);
|
||||
|
||||
Node* osr_phi =
|
||||
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant(),
|
||||
T.jsgraph.ZeroConstant());
|
||||
|
||||
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false2);
|
||||
T.graph.SetEnd(ret);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
CheckInputs(loop, T.start, if_false1, if_true2);
|
||||
CheckInputs(branch1, T.p0, loop);
|
||||
CheckInputs(branch2, T.p0, if_true1);
|
||||
CheckInputs(if_true1, branch1);
|
||||
CheckInputs(if_false1, branch1);
|
||||
CheckInputs(if_true2, branch2);
|
||||
CheckInputs(if_false2, branch2);
|
||||
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(),
|
||||
T.jsgraph.ZeroConstant(), loop);
|
||||
CheckInputs(ret, osr_phi, T.start, if_false2);
|
||||
}
|
||||
|
||||
|
||||
struct While {
|
||||
OsrDeconstructorTester& t;
|
||||
Node* branch;
|
||||
Node* if_true;
|
||||
Node* exit;
|
||||
Node* loop;
|
||||
|
||||
While(OsrDeconstructorTester& R, Node* cond, bool is_osr, int backedges = 1)
|
||||
: t(R) {
|
||||
loop = t.NewLoop(is_osr, backedges);
|
||||
branch = t.graph.NewNode(t.common.Branch(), cond, loop);
|
||||
if_true = t.graph.NewNode(t.common.IfTrue(), branch);
|
||||
exit = t.graph.NewNode(t.common.IfFalse(), branch);
|
||||
loop->ReplaceInput(loop->InputCount() - 1, if_true);
|
||||
}
|
||||
|
||||
void Nest(While& that) {
|
||||
that.loop->ReplaceInput(that.loop->InputCount() - 1, exit);
|
||||
this->loop->ReplaceInput(0, that.if_true);
|
||||
}
|
||||
|
||||
Node* Phi(Node* i1, Node* i2, Node* i3) {
|
||||
if (loop->InputCount() == 2) {
|
||||
return t.graph.NewNode(t.common.Phi(MachineRepresentation::kTagged, 2),
|
||||
i1, i2, loop);
|
||||
} else {
|
||||
return t.graph.NewNode(t.common.Phi(MachineRepresentation::kTagged, 3),
|
||||
i1, i2, i3, loop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Node* FindSuccessor(Node* node, IrOpcode::Value opcode) {
|
||||
for (Node* use : node->uses()) {
|
||||
if (use->opcode() == opcode) return use;
|
||||
}
|
||||
UNREACHABLE(); // should have been found.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_nested1) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
While outer(T, T.p0, false);
|
||||
While inner(T, T.p0, true);
|
||||
inner.Nest(outer);
|
||||
|
||||
Node* outer_phi = outer.Phi(T.p0, T.p0, nullptr);
|
||||
outer.branch->ReplaceInput(0, outer_phi);
|
||||
|
||||
Node* osr_phi = inner.Phi(T.jsgraph.TrueConstant(), T.osr_values[0],
|
||||
T.jsgraph.FalseConstant());
|
||||
inner.branch->ReplaceInput(0, osr_phi);
|
||||
outer_phi->ReplaceInput(1, osr_phi);
|
||||
|
||||
Node* ret =
|
||||
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
|
||||
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
||||
T.graph.SetEnd(end);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
// Check structure of deconstructed graph.
|
||||
// Check inner OSR loop is directly connected to start.
|
||||
CheckInputs(inner.loop, T.start, inner.if_true);
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.FalseConstant(), inner.loop);
|
||||
|
||||
// Check control transfer to copy of outer loop.
|
||||
Node* new_outer_loop = FindSuccessor(inner.exit, IrOpcode::kLoop);
|
||||
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
|
||||
CHECK_NE(new_outer_loop, outer.loop);
|
||||
CHECK_NE(new_outer_phi, outer_phi);
|
||||
|
||||
CheckInputs(new_outer_loop, inner.exit, new_outer_loop->InputAt(1));
|
||||
|
||||
// Check structure of outer loop.
|
||||
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
|
||||
CHECK_NE(new_outer_branch, outer.branch);
|
||||
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
|
||||
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
|
||||
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
|
||||
|
||||
// Check structure of return.
|
||||
end = T.graph.end();
|
||||
Node* new_ret = end->InputAt(0);
|
||||
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
||||
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
|
||||
|
||||
// Check structure of inner loop.
|
||||
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
|
||||
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
|
||||
|
||||
CheckInputs(new_inner_phi, T.jsgraph.TrueConstant(),
|
||||
T.jsgraph.FalseConstant(), new_inner_loop);
|
||||
CheckInputs(new_outer_phi, osr_phi, new_inner_phi, new_outer_loop);
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_nested2) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
// Test multiple backedge outer loop.
|
||||
While outer(T, T.p0, false, 2);
|
||||
While inner(T, T.p0, true);
|
||||
inner.Nest(outer);
|
||||
|
||||
Node* outer_phi = outer.Phi(T.p0, T.p0, T.p0);
|
||||
outer.branch->ReplaceInput(0, outer_phi);
|
||||
|
||||
Node* osr_phi = inner.Phi(T.jsgraph.TrueConstant(), T.osr_values[0],
|
||||
T.jsgraph.FalseConstant());
|
||||
inner.branch->ReplaceInput(0, osr_phi);
|
||||
outer_phi->ReplaceInput(1, osr_phi);
|
||||
outer_phi->ReplaceInput(2, T.jsgraph.FalseConstant());
|
||||
|
||||
Node* x_branch = T.graph.NewNode(T.common.Branch(), osr_phi, inner.exit);
|
||||
Node* x_true = T.graph.NewNode(T.common.IfTrue(), x_branch);
|
||||
Node* x_false = T.graph.NewNode(T.common.IfFalse(), x_branch);
|
||||
|
||||
outer.loop->ReplaceInput(1, x_true);
|
||||
outer.loop->ReplaceInput(2, x_false);
|
||||
|
||||
Node* ret =
|
||||
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
|
||||
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
||||
T.graph.SetEnd(end);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
// Check structure of deconstructed graph.
|
||||
// Check inner OSR loop is directly connected to start.
|
||||
CheckInputs(inner.loop, T.start, inner.if_true);
|
||||
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.FalseConstant(), inner.loop);
|
||||
|
||||
// Check control transfer to copy of outer loop.
|
||||
Node* new_merge = FindSuccessor(x_true, IrOpcode::kMerge);
|
||||
CHECK_EQ(new_merge, FindSuccessor(x_false, IrOpcode::kMerge));
|
||||
CheckInputs(new_merge, x_true, x_false);
|
||||
|
||||
Node* new_outer_loop = FindSuccessor(new_merge, IrOpcode::kLoop);
|
||||
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
|
||||
CHECK_NE(new_outer_loop, outer.loop);
|
||||
CHECK_NE(new_outer_phi, outer_phi);
|
||||
|
||||
Node* new_entry_phi = FindSuccessor(new_merge, IrOpcode::kPhi);
|
||||
CheckInputs(new_entry_phi, osr_phi, T.jsgraph.FalseConstant(), new_merge);
|
||||
|
||||
CHECK_EQ(new_merge, new_outer_loop->InputAt(0));
|
||||
|
||||
// Check structure of outer loop.
|
||||
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
|
||||
CHECK_NE(new_outer_branch, outer.branch);
|
||||
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
|
||||
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
|
||||
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
|
||||
|
||||
// Check structure of return.
|
||||
end = T.graph.end();
|
||||
Node* new_ret = end->InputAt(0);
|
||||
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
||||
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
|
||||
|
||||
// Check structure of inner loop.
|
||||
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
|
||||
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
|
||||
|
||||
CheckInputs(new_inner_phi, T.jsgraph.TrueConstant(),
|
||||
T.jsgraph.FalseConstant(), new_inner_loop);
|
||||
CheckInputs(new_outer_phi, new_entry_phi, new_inner_phi,
|
||||
T.jsgraph.FalseConstant(), new_outer_loop);
|
||||
}
|
||||
|
||||
|
||||
Node* MakeCounter(JSGraph* jsgraph, Node* start, Node* loop) {
|
||||
int count = loop->InputCount();
|
||||
NodeVector tmp_inputs(jsgraph->graph()->zone());
|
||||
for (int i = 0; i < count; i++) {
|
||||
tmp_inputs.push_back(start);
|
||||
}
|
||||
tmp_inputs.push_back(loop);
|
||||
|
||||
Node* phi = jsgraph->graph()->NewNode(
|
||||
jsgraph->common()->Phi(MachineRepresentation::kWord32, count), count + 1,
|
||||
&tmp_inputs[0]);
|
||||
Node* inc = jsgraph->graph()->NewNode(&kIntAdd, phi, jsgraph->OneConstant());
|
||||
|
||||
for (int i = 1; i < count; i++) {
|
||||
phi->ReplaceInput(i, inc);
|
||||
}
|
||||
return phi;
|
||||
}
|
||||
|
||||
|
||||
TEST(Deconstruct_osr_nested3) {
|
||||
OsrDeconstructorTester T(1);
|
||||
|
||||
// outermost loop.
|
||||
While loop0(T, T.p0, false, 1);
|
||||
Node* loop0_cntr = MakeCounter(&T.jsgraph, T.p0, loop0.loop);
|
||||
loop0.branch->ReplaceInput(0, loop0_cntr);
|
||||
|
||||
// middle loop.
|
||||
Node* loop1 = T.graph.NewNode(T.common.Loop(1), loop0.if_true);
|
||||
Node* loop1_phi =
|
||||
T.graph.NewNode(T.common.Phi(MachineRepresentation::kTagged, 2),
|
||||
loop0_cntr, loop0_cntr, loop1);
|
||||
|
||||
// innermost (OSR) loop.
|
||||
While loop2(T, T.p0, true, 1);
|
||||
loop2.loop->ReplaceInput(0, loop1);
|
||||
|
||||
Node* loop2_cntr = MakeCounter(&T.jsgraph, loop1_phi, loop2.loop);
|
||||
loop2_cntr->ReplaceInput(1, T.osr_values[0]);
|
||||
Node* osr_phi = loop2_cntr;
|
||||
Node* loop2_inc = loop2_cntr->InputAt(2);
|
||||
loop2.branch->ReplaceInput(0, loop2_cntr);
|
||||
|
||||
loop1_phi->ReplaceInput(1, loop2_cntr);
|
||||
loop0_cntr->ReplaceInput(1, loop2_cntr);
|
||||
|
||||
// Branch to either the outer or middle loop.
|
||||
Node* branch = T.graph.NewNode(T.common.Branch(), loop2_cntr, loop2.exit);
|
||||
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
|
||||
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
|
||||
|
||||
loop0.loop->ReplaceInput(1, if_true);
|
||||
loop1->AppendInput(T.graph.zone(), if_false);
|
||||
NodeProperties::ChangeOp(loop1, T.common.Loop(2));
|
||||
|
||||
Node* ret =
|
||||
T.graph.NewNode(T.common.Return(), loop0_cntr, T.start, loop0.exit);
|
||||
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
||||
T.graph.SetEnd(end);
|
||||
|
||||
T.DeconstructOsr();
|
||||
|
||||
// Check structure of deconstructed graph.
|
||||
// Check loop2 (OSR loop) is directly connected to start.
|
||||
CheckInputs(loop2.loop, T.start, loop2.if_true);
|
||||
CheckInputs(osr_phi, T.osr_values[0], loop2_inc, loop2.loop);
|
||||
CheckInputs(loop2.branch, osr_phi, loop2.loop);
|
||||
CheckInputs(loop2.if_true, loop2.branch);
|
||||
CheckInputs(loop2.exit, loop2.branch);
|
||||
CheckInputs(branch, osr_phi, loop2.exit);
|
||||
CheckInputs(if_true, branch);
|
||||
CheckInputs(if_false, branch);
|
||||
|
||||
// Check structure of new_loop1.
|
||||
Node* new_loop1_loop = FindSuccessor(if_false, IrOpcode::kLoop);
|
||||
// TODO(titzer): check the internal copy of loop2.
|
||||
USE(new_loop1_loop);
|
||||
|
||||
// Check structure of new_loop0.
|
||||
Node* new_loop0_loop_entry = FindSuccessor(if_true, IrOpcode::kMerge);
|
||||
Node* new_loop0_loop = FindSuccessor(new_loop0_loop_entry, IrOpcode::kLoop);
|
||||
// TODO(titzer): check the internal copies of loop1 and loop2.
|
||||
|
||||
Node* new_loop0_branch = FindSuccessor(new_loop0_loop, IrOpcode::kBranch);
|
||||
Node* new_loop0_if_true = FindSuccessor(new_loop0_branch, IrOpcode::kIfTrue);
|
||||
Node* new_loop0_exit = FindSuccessor(new_loop0_branch, IrOpcode::kIfFalse);
|
||||
|
||||
USE(new_loop0_if_true);
|
||||
|
||||
Node* new_ret = T.graph.end()->InputAt(0);
|
||||
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
||||
|
||||
Node* new_loop0_phi = new_ret->InputAt(0);
|
||||
CHECK_EQ(IrOpcode::kPhi, new_loop0_phi->opcode());
|
||||
CHECK_EQ(new_loop0_loop, NodeProperties::GetControlInput(new_loop0_phi));
|
||||
CHECK_EQ(new_loop0_phi, FindSuccessor(new_loop0_loop, IrOpcode::kPhi));
|
||||
|
||||
// Check that the return returns the phi from the OSR loop and control
|
||||
// depends on the copy of the outer loop0.
|
||||
CheckInputs(new_ret, new_loop0_phi, T.graph.start(), new_loop0_exit);
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -1,119 +0,0 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-debug-as debug
|
||||
|
||||
var Debug = debug.Debug;
|
||||
|
||||
var changed = false;
|
||||
|
||||
function listenerSetJToResult(
|
||||
event, exec_state, event_data, data) {
|
||||
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
var scope = exec_state.frame(1).scope(0);
|
||||
var newval = "result";
|
||||
try {
|
||||
scope.setVariableValue("j", newval);
|
||||
changed = true;
|
||||
} catch(e) {
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.setListener(listenerSetJToResult);
|
||||
|
||||
function g() { debugger; }
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
function ChangeSmiConstantAndOsr() {
|
||||
var j = 1;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if (i == 2) {
|
||||
%OptimizeOsr();
|
||||
g();
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
var r1 = ChangeSmiConstantAndOsr();
|
||||
if (changed) {
|
||||
assertEquals("result", r1);
|
||||
} else {
|
||||
assertEquals(1, r1);
|
||||
}
|
||||
|
||||
function ChangeFloatConstantAndOsr() {
|
||||
var j = 0.1;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
if (i == 2) {
|
||||
%OptimizeOsr();
|
||||
g();
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
var r2 = ChangeFloatConstantAndOsr();
|
||||
if (changed) {
|
||||
assertEquals("result", r2);
|
||||
} else {
|
||||
assertEquals(0.1, r2);
|
||||
}
|
||||
|
||||
function ChangeFloatVarAndOsr() {
|
||||
var j = 0.1;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
j = j + 0.1;
|
||||
if (i == 2) {
|
||||
%OptimizeOsr();
|
||||
g();
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
var r3 = ChangeFloatVarAndOsr();
|
||||
if (changed) {
|
||||
assertEquals("result0.1", r3);
|
||||
} else {
|
||||
assertEquals(0.5, r3);
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
var o = { toString : function() { counter++; return 100; } };
|
||||
|
||||
function listenerSetJToObject(
|
||||
event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
var scope = exec_state.frame(1).scope(0);
|
||||
try {
|
||||
scope.setVariableValue("j", o);
|
||||
changed = true;
|
||||
} catch(e) {
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.setListener(listenerSetJToObject);
|
||||
|
||||
function ChangeIntVarAndOsr() {
|
||||
var j = 1;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
j = j + 1|0;
|
||||
if (i == 2) {
|
||||
%OptimizeOsr();
|
||||
g();
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
var r4 = ChangeIntVarAndOsr();
|
||||
if (changed) {
|
||||
assertEquals(101, r4);
|
||||
assertEquals(1, counter);
|
||||
} else {
|
||||
assertEquals(5, r4);
|
||||
}
|
Loading…
Reference in New Issue
Block a user