Commit 1314406c authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[interpreter] Implement OSR graph construction from bytecode.

This implements graph construction for entry via on-stack replacement
within the {BytecodeGraphBuilder}. Entry points are at loop headers
similar to previous OSR implementations. All interpreter registers are
addressable via {OsrValue} nodes in the graph. Currently we rely on
{OsrPoll} bytecodes to be placed right after loop headers (i.e. at the
targets of back edges).

R=jarin@chromium.org
BUG=v8:4764

Review-Url: https://codereview.chromium.org/2171083004
Cr-Commit-Position: refs/heads/master@{#38083}
parent 0bb05780
......@@ -587,6 +587,10 @@ void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
// Frame specialization implies function context specialization.
DCHECK(!info->is_frame_specializing());
// TODO(4764): When compiling for OSR from bytecode, BailoutId might derive
// from bytecode offset and overlap with actual BailoutId. No caching!
if (info->is_osr() && info->is_optimizing_from_bytecode()) return;
// Cache optimized context-specific code.
Handle<JSFunction> function = info->closure();
Handle<SharedFunctionInfo> shared(function->shared());
......@@ -747,13 +751,18 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
Isolate* isolate = function->GetIsolate();
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
// TODO(4764): Remove this guard once OSR graph construction works.
if (!osr_ast_id.IsNone() && osr_frame->is_interpreted()) {
return MaybeHandle<Code>();
}
bool ignition_osr = osr_frame && osr_frame->is_interpreted();
DCHECK_IMPLIES(ignition_osr, !osr_ast_id.IsNone());
DCHECK_IMPLIES(ignition_osr, FLAG_ignition_osr);
// Flag combination --ignition-osr --no-turbo-from-bytecode is unsupported.
if (ignition_osr && !FLAG_turbo_from_bytecode) return MaybeHandle<Code>();
Handle<Code> cached_code;
if (GetCodeFromOptimizedCodeMap(function, osr_ast_id)
// TODO(4764): When compiling for OSR from bytecode, BailoutId might derive
// from bytecode offset and overlap with actual BailoutId. No lookup!
if (!ignition_osr &&
GetCodeFromOptimizedCodeMap(function, osr_ast_id)
.ToHandle(&cached_code)) {
if (FLAG_trace_opt) {
PrintF("[found optimized code for ");
......@@ -774,7 +783,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
VMState<COMPILER> state(isolate);
DCHECK(!isolate->has_pending_exception());
PostponeInterruptsScope postpone(isolate);
bool use_turbofan = UseTurboFan(shared);
bool use_turbofan = UseTurboFan(shared) || ignition_osr;
std::unique_ptr<CompilationJob> job(
use_turbofan ? compiler::Pipeline::NewCompilationJob(function)
: new HCompilationJob(function));
......
......@@ -60,6 +60,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
Environment* CopyForConditional() const;
Environment* CopyForLoop();
void Merge(Environment* other);
void PrepareForOsr();
private:
explicit Environment(const Environment* copy);
......@@ -358,6 +359,36 @@ void BytecodeGraphBuilder::Environment::PrepareForLoop() {
builder()->exit_controls_.push_back(terminate);
}
void BytecodeGraphBuilder::Environment::PrepareForOsr() {
DCHECK_EQ(IrOpcode::kLoop, GetControlDependency()->opcode());
DCHECK_EQ(1, GetControlDependency()->InputCount());
Node* start = graph()->start();
// Create a control node for the OSR entry point and merge it into the loop
// header. Update the current environment's control dependency accordingly.
Node* entry = graph()->NewNode(common()->OsrLoopEntry(), start, start);
Node* control = builder()->MergeControl(GetControlDependency(), entry);
UpdateControlDependency(control);
// Create a merge of the effect from the OSR entry and the existing effect
// dependency. Update the current environment's effect dependency accordingly.
Node* effect = builder()->MergeEffect(GetEffectDependency(), entry, control);
UpdateEffectDependency(effect);
// Rename all values in the environment which will extend or introduce Phi
// nodes to contain the OSR values available at the entry point.
Node* osr_context = graph()->NewNode(
common()->OsrValue(Linkage::kOsrContextSpillSlotIndex), entry);
context_ = builder()->MergeValue(context_, osr_context, control);
int size = static_cast<int>(values()->size());
for (int i = 0; i < size; i++) {
int idx = i; // Indexing scheme follows {StandardFrame}, adapt accordingly.
if (i >= register_base()) idx += InterpreterFrameConstants::kExtraSlotCount;
if (i >= accumulator_base()) idx = Linkage::kOsrAccumulatorRegisterIndex;
Node* osr_value = graph()->NewNode(common()->OsrValue(idx), entry);
values_[i] = builder()->MergeValue(values_[i], osr_value, control);
}
}
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
Node** state_values, int offset, int count) {
......@@ -447,6 +478,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone,
FrameStateType::kInterpretedFunction,
bytecode_array()->parameter_count(),
bytecode_array()->register_count(), info->shared_info())),
osr_ast_id_(info->osr_ast_id()),
merge_environments_(local_zone),
exception_handlers_(local_zone),
current_exception_handler_(0),
......@@ -521,6 +553,10 @@ bool BytecodeGraphBuilder::CreateGraph() {
GetFunctionContext());
set_environment(&env);
// For OSR add an {OsrNormalEntry} as the start of the top-level environment.
// It will be replaced with {Dead} after typing and optimizations.
if (!osr_ast_id_.IsNone()) NewNode(common()->OsrNormalEntry());
VisitBytecodes();
// Finish the basic structure of the graph.
......@@ -1393,8 +1429,12 @@ void BytecodeGraphBuilder::VisitStackCheck() {
}
void BytecodeGraphBuilder::VisitOsrPoll() {
// TODO(4764): Implement OSR graph construction. Not marked UNIMPLEMENTED to
// ensure the --ignition-osr flag can already be fuzzed without crashing.
// TODO(4764): This should be moved into the {VisitBytecodes} once we merge
// the polling with existing bytecode. This will also guarantee that we are
// not missing the OSR entry point, which we wouldn't catch right now.
if (osr_ast_id_.ToInt() == bytecode_iterator().current_offset()) {
environment()->PrepareForOsr();
}
}
void BytecodeGraphBuilder::VisitReturn() {
......
......@@ -219,6 +219,7 @@ class BytecodeGraphBuilder {
const interpreter::BytecodeArrayIterator* bytecode_iterator_;
const BytecodeBranchAnalysis* branch_analysis_;
Environment* environment_;
BailoutId osr_ast_id_;
// Merge environments are snapshots of the environment at points where the
// control flow merges. This models a forward data flow propagation of all
......
......@@ -422,6 +422,9 @@ class Linkage : public ZoneObject {
// A special {OsrValue} index to indicate the context spill slot.
static const int kOsrContextSpillSlotIndex = -1;
// A special {OsrValue} index to indicate the accumulator register.
static const int kOsrAccumulatorRegisterIndex = -1;
private:
CallDescriptor* const incoming_;
......
......@@ -24,10 +24,16 @@ namespace internal {
namespace compiler {
OsrHelper::OsrHelper(CompilationInfo* info)
: parameter_count_(info->scope()->num_parameters()),
stack_slot_count_(info->scope()->num_stack_slots() +
info->osr_expr_stack_height()) {}
: parameter_count_(
info->is_optimizing_from_bytecode()
? info->shared_info()->bytecode_array()->parameter_count()
: info->scope()->num_parameters()),
stack_slot_count_(
info->is_optimizing_from_bytecode()
? info->shared_info()->bytecode_array()->register_count() +
InterpreterFrameConstants::kExtraSlotCount
: info->scope()->num_stack_slots() +
info->osr_expr_stack_height()) {}
#ifdef DEBUG
#define TRACE_COND (FLAG_trace_turbo_graph && FLAG_trace_osr)
......
......@@ -1820,6 +1820,7 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
data->InitializeRegisterAllocationData(config, descriptor);
if (info()->is_osr()) {
AllowHandleDereference allow_deref;
OsrHelper osr_helper(info());
osr_helper.SetupFrame(data->frame());
}
......
......@@ -334,7 +334,7 @@ class BuiltinExitFrameConstants : public CommonFrameConstants {
class InterpreterFrameConstants : public AllStatic {
public:
// Fixed frame includes new.target and bytecode offset.
// Fixed frame includes new.target, bytecode array, and bytecode offset.
static const int kFixedFrameSize =
StandardFrameConstants::kFixedFrameSize + 3 * kPointerSize;
static const int kFixedFrameSizeFromFp =
......@@ -355,6 +355,11 @@ class InterpreterFrameConstants : public AllStatic {
static const int kExpressionsOffset = kRegisterFileFromFp;
// Number of fixed slots in addition to a {StandardFrame}.
static const int kExtraSlotCount =
InterpreterFrameConstants::kFixedFrameSize / kPointerSize -
StandardFrameConstants::kFixedFrameSize / kPointerSize;
// Expression index for {StandardFrame::GetExpressionAddress}.
static const int kBytecodeArrayExpressionIndex = -2;
static const int kBytecodeOffsetExpressionIndex = -1;
......
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