Commit c0a63243 authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[maglev] Start implenting inlining

Add a --maglev-inlining flag, and add some half-baked support for
inlining functions when there is call feedback.

When the flag is enabled and there is call feedback, we create a nested
MaglevGraphBuilder for the current graph, and pause building the graph
of the outer function. We manually set up its prologue to set up its
frame with the arguments pass into the call, build the body with the
nested graph builder. This inner builder knows that it is building an
inlined function, and all Return bytecodes will instead emit a Jump to a
single merge block at the end of the function, where execution of the
outer function can resume.

These inner function basic blocks are wired into the outer graph with
new JumpToInline and JumpFromInline control nodes. The idea is that
subsequent passes will know what the inline function is, and will use
these to manage the function stack (particularly for codegen and
especially deopts).

Bug: v8:7700
Change-Id: I4e9b153f8cf4d06c56e7be6365e7a18b86a773c0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3585958
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarJakob Linke <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80077}
parent f98e1f4d
...@@ -511,6 +511,8 @@ DEFINE_BOOL(future, FUTURE_BOOL, ...@@ -511,6 +511,8 @@ DEFINE_BOOL(future, FUTURE_BOOL,
#ifdef V8_ENABLE_MAGLEV #ifdef V8_ENABLE_MAGLEV
#define V8_ENABLE_MAGLEV_BOOL true #define V8_ENABLE_MAGLEV_BOOL true
DEFINE_BOOL(maglev, false, "enable the maglev optimizing compiler") DEFINE_BOOL(maglev, false, "enable the maglev optimizing compiler")
DEFINE_BOOL(maglev_inlining, false,
"enable inlining in the maglev optimizing compiler")
#else #else
#define V8_ENABLE_MAGLEV_BOOL false #define V8_ENABLE_MAGLEV_BOOL false
DEFINE_BOOL_READONLY(maglev, false, "enable the maglev optimizing compiler") DEFINE_BOOL_READONLY(maglev, false, "enable the maglev optimizing compiler")
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/maglev/maglev-code-generator.h" #include "src/maglev/maglev-code-generator.h"
#include "src/base/hashmap.h"
#include "src/codegen/code-desc.h" #include "src/codegen/code-desc.h"
#include "src/codegen/register.h" #include "src/codegen/register.h"
#include "src/codegen/safepoint-table.h" #include "src/codegen/safepoint-table.h"
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
#include "src/maglev/maglev-ir.h" #include "src/maglev/maglev-ir.h"
#include "src/maglev/maglev-regalloc-data.h" #include "src/maglev/maglev-regalloc-data.h"
#include "src/objects/code-inl.h" #include "src/objects/code-inl.h"
#include "src/utils/identity-map.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -318,15 +320,15 @@ class MaglevCodeGeneratorImpl final { ...@@ -318,15 +320,15 @@ class MaglevCodeGeneratorImpl final {
} }
private: private:
static constexpr int kFunctionLiteralIndex = 0; static constexpr int kOptimizedOutConstantIndex = 0;
static constexpr int kOptimizedOutConstantIndex = 1;
MaglevCodeGeneratorImpl(MaglevCompilationInfo* compilation_info, Graph* graph) MaglevCodeGeneratorImpl(MaglevCompilationInfo* compilation_info, Graph* graph)
: safepoint_table_builder_(compilation_info->zone()), : safepoint_table_builder_(compilation_info->zone()),
translation_array_builder_(compilation_info->zone()), translation_array_builder_(compilation_info->zone()),
code_gen_state_(compilation_info, safepoint_table_builder()), code_gen_state_(compilation_info, safepoint_table_builder()),
processor_(compilation_info, &code_gen_state_), processor_(compilation_info, &code_gen_state_),
graph_(graph) {} graph_(graph),
deopt_literals_(compilation_info->isolate()->heap()) {}
MaybeHandle<Code> Generate() { MaybeHandle<Code> Generate() {
EmitCode(); EmitCode();
...@@ -353,6 +355,14 @@ class MaglevCodeGeneratorImpl final { ...@@ -353,6 +355,14 @@ class MaglevCodeGeneratorImpl final {
void EmitDeopts() { void EmitDeopts() {
deopt_exit_start_offset_ = __ pc_offset(); deopt_exit_start_offset_ = __ pc_offset();
// We'll emit the optimized out constant a bunch of times, so to avoid
// looking it up in the literal map every time, add it now with the fixed
// offset 0.
int optimized_out_constant_index =
GetDeoptLiteral(ReadOnlyRoots(isolate()).optimized_out());
USE(optimized_out_constant_index);
DCHECK_EQ(kOptimizedOutConstantIndex, optimized_out_constant_index);
__ RecordComment("-- Non-lazy deopts"); __ RecordComment("-- Non-lazy deopts");
for (EagerDeoptInfo* deopt_info : code_gen_state_.eager_deopts()) { for (EagerDeoptInfo* deopt_info : code_gen_state_.eager_deopts()) {
EmitEagerDeopt(deopt_info); EmitEagerDeopt(deopt_info);
...@@ -381,39 +391,51 @@ class MaglevCodeGeneratorImpl final { ...@@ -381,39 +391,51 @@ class MaglevCodeGeneratorImpl final {
} }
} }
void EmitEagerDeopt(EagerDeoptInfo* deopt_info) { const InputLocation* EmitDeoptFrame(const MaglevCompilationUnit& unit,
int frame_count = 1; const CheckpointedInterpreterState& state,
int jsframe_count = 1; const InputLocation* input_locations) {
int update_feedback_count = 0; if (state.parent) {
deopt_info->deopt_index = translation_array_builder_.BeginTranslation( // Deopt input locations are in the order of deopt frame emission, so
frame_count, jsframe_count, update_feedback_count); // update the pointer after emitting the parent frame.
input_locations =
const MaglevCompilationUnit& compilation_unit = EmitDeoptFrame(*unit.caller(), *state.parent, input_locations);
*code_gen_state_.compilation_info()->toplevel_compilation_unit(); }
// Returns are used for updating an accumulator or register after a lazy // Returns are used for updating an accumulator or register after a lazy
// deopt. // deopt.
const int return_offset = 0; const int return_offset = 0;
const int return_count = 0; const int return_count = 0;
translation_array_builder_.BeginInterpretedFrame( translation_array_builder_.BeginInterpretedFrame(
deopt_info->state.bytecode_position, kFunctionLiteralIndex, state.bytecode_position,
compilation_unit.register_count(), return_offset, return_count); GetDeoptLiteral(*unit.shared_function_info().object()),
unit.register_count(), return_offset, return_count);
EmitDeoptFrameValues(compilation_unit, deopt_info->state.register_frame, return EmitDeoptFrameValues(unit, state.register_frame, input_locations,
deopt_info->input_locations, interpreter::Register::invalid_value());
interpreter::Register::invalid_value()); }
void EmitEagerDeopt(EagerDeoptInfo* deopt_info) {
int frame_count = 1 + deopt_info->unit.inlining_depth();
int jsframe_count = frame_count;
int update_feedback_count = 0;
deopt_info->deopt_index = translation_array_builder_.BeginTranslation(
frame_count, jsframe_count, update_feedback_count);
EmitDeoptFrame(deopt_info->unit, deopt_info->state,
deopt_info->input_locations);
} }
void EmitLazyDeopt(LazyDeoptInfo* deopt_info) { void EmitLazyDeopt(LazyDeoptInfo* deopt_info) {
const MaglevCompilationUnit& unit = deopt_info->unit;
DCHECK_NULL(unit.caller());
DCHECK_EQ(unit.inlining_depth(), 0);
int frame_count = 1; int frame_count = 1;
int jsframe_count = 1; int jsframe_count = 1;
int update_feedback_count = 0; int update_feedback_count = 0;
deopt_info->deopt_index = translation_array_builder_.BeginTranslation( deopt_info->deopt_index = translation_array_builder_.BeginTranslation(
frame_count, jsframe_count, update_feedback_count); frame_count, jsframe_count, update_feedback_count);
const MaglevCompilationUnit& compilation_unit =
*code_gen_state_.compilation_info()->toplevel_compilation_unit();
// Return offsets are counted from the end of the translation frame, which // Return offsets are counted from the end of the translation frame, which
// is the array [parameters..., locals..., accumulator]. // is the array [parameters..., locals..., accumulator].
int return_offset; int return_offset;
...@@ -429,20 +451,20 @@ class MaglevCodeGeneratorImpl final { ...@@ -429,20 +451,20 @@ class MaglevCodeGeneratorImpl final {
// ^ // ^
// and this calculation gives, correctly: // and this calculation gives, correctly:
// 2 + 2 - 1 = 3 // 2 + 2 - 1 = 3
return_offset = compilation_unit.register_count() + return_offset = unit.register_count() + unit.parameter_count() -
compilation_unit.parameter_count() -
deopt_info->result_location.ToParameterIndex(); deopt_info->result_location.ToParameterIndex();
} else { } else {
return_offset = compilation_unit.register_count() - return_offset =
deopt_info->result_location.index(); unit.register_count() - deopt_info->result_location.index();
} }
// TODO(leszeks): Support lazy deopts with multiple return values. // TODO(leszeks): Support lazy deopts with multiple return values.
int return_count = 1; int return_count = 1;
translation_array_builder_.BeginInterpretedFrame( translation_array_builder_.BeginInterpretedFrame(
deopt_info->state.bytecode_position, kFunctionLiteralIndex, deopt_info->state.bytecode_position,
compilation_unit.register_count(), return_offset, return_count); GetDeoptLiteral(*unit.shared_function_info().object()),
unit.register_count(), return_offset, return_count);
EmitDeoptFrameValues(compilation_unit, deopt_info->state.register_frame, EmitDeoptFrameValues(unit, deopt_info->state.register_frame,
deopt_info->input_locations, deopt_info->input_locations,
deopt_info->result_location); deopt_info->result_location);
} }
...@@ -500,15 +522,20 @@ class MaglevCodeGeneratorImpl final { ...@@ -500,15 +522,20 @@ class MaglevCodeGeneratorImpl final {
code_gen_state_.GetFramePointerOffsetForStackSlot(operand)); code_gen_state_.GetFramePointerOffsetForStackSlot(operand));
} }
void EmitDeoptFrameValues( const InputLocation* EmitDeoptFrameValues(
const MaglevCompilationUnit& compilation_unit, const MaglevCompilationUnit& compilation_unit,
const CompactInterpreterFrameState* checkpoint_state, const CompactInterpreterFrameState* checkpoint_state,
const InputLocation* input_locations, const InputLocation* input_locations,
interpreter::Register result_location) { interpreter::Register result_location) {
// Closure // Closure
int closure_index = DeoptStackSlotIndexFromFPOffset( if (compilation_unit.inlining_depth() == 0) {
StandardFrameConstants::kFunctionOffset); int closure_index = DeoptStackSlotIndexFromFPOffset(
translation_array_builder_.StoreStackSlot(closure_index); StandardFrameConstants::kFunctionOffset);
translation_array_builder_.StoreStackSlot(closure_index);
} else {
translation_array_builder_.StoreLiteral(
GetDeoptLiteral(*compilation_unit.function().object()));
}
// TODO(leszeks): The input locations array happens to be in the same order // TODO(leszeks): The input locations array happens to be in the same order
// as parameters+locals+accumulator are accessed here. We should make this // as parameters+locals+accumulator are accessed here. We should make this
...@@ -573,6 +600,8 @@ class MaglevCodeGeneratorImpl final { ...@@ -573,6 +600,8 @@ class MaglevCodeGeneratorImpl final {
translation_array_builder_.StoreLiteral(kOptimizedOutConstantIndex); translation_array_builder_.StoreLiteral(kOptimizedOutConstantIndex);
} }
} }
return input_location;
} }
void EmitMetadata() { void EmitMetadata() {
...@@ -610,6 +639,7 @@ class MaglevCodeGeneratorImpl final { ...@@ -610,6 +639,7 @@ class MaglevCodeGeneratorImpl final {
translation_array_builder_.ToTranslationArray(isolate()->factory()); translation_array_builder_.ToTranslationArray(isolate()->factory());
data->SetTranslationByteArray(*translation_array); data->SetTranslationByteArray(*translation_array);
// TODO(leszeks): Fix with the real inlined function count.
data->SetInlinedFunctionCount(Smi::zero()); data->SetInlinedFunctionCount(Smi::zero());
// TODO(leszeks): Support optimization IDs // TODO(leszeks): Support optimization IDs
data->SetOptimizationId(Smi::zero()); data->SetOptimizationId(Smi::zero());
...@@ -624,18 +654,17 @@ class MaglevCodeGeneratorImpl final { ...@@ -624,18 +654,17 @@ class MaglevCodeGeneratorImpl final {
->shared_function_info() ->shared_function_info()
.object()); .object());
// TODO(leszeks): Proper literals array.
Handle<DeoptimizationLiteralArray> literals = Handle<DeoptimizationLiteralArray> literals =
isolate()->factory()->NewDeoptimizationLiteralArray(2); isolate()->factory()->NewDeoptimizationLiteralArray(
literals->set(kFunctionLiteralIndex, *code_gen_state_.compilation_info() deopt_literals_.size());
->toplevel_compilation_unit() IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope iterate(
->shared_function_info() &deopt_literals_);
.object()); for (auto it = iterate.begin(); it != iterate.end(); ++it) {
literals->set(kOptimizedOutConstantIndex, literals->set(*it.entry(), it.key());
ReadOnlyRoots(isolate()).optimized_out()); }
data->SetLiteralArray(*literals); data->SetLiteralArray(*literals);
// TODO(leszeks): Fix once we have inlining. // TODO(leszeks): Fix with the real inlining positions.
Handle<PodArray<InliningPosition>> inlining_positions = Handle<PodArray<InliningPosition>> inlining_positions =
PodArray<InliningPosition>::New(isolate(), 0); PodArray<InliningPosition>::New(isolate(), 0);
data->SetInliningPositions(*inlining_positions); data->SetInliningPositions(*inlining_positions);
...@@ -687,11 +716,21 @@ class MaglevCodeGeneratorImpl final { ...@@ -687,11 +716,21 @@ class MaglevCodeGeneratorImpl final {
return &translation_array_builder_; return &translation_array_builder_;
} }
int GetDeoptLiteral(Object obj) {
IdentityMapFindResult<int> res = deopt_literals_.FindOrInsert(obj);
if (!res.already_exists) {
DCHECK_EQ(0, *res.entry);
*res.entry = deopt_literals_.size() - 1;
}
return *res.entry;
}
SafepointTableBuilder safepoint_table_builder_; SafepointTableBuilder safepoint_table_builder_;
TranslationArrayBuilder translation_array_builder_; TranslationArrayBuilder translation_array_builder_;
MaglevCodeGenState code_gen_state_; MaglevCodeGenState code_gen_state_;
GraphProcessor<MaglevCodeGeneratingNodeProcessor> processor_; GraphProcessor<MaglevCodeGeneratingNodeProcessor> processor_;
Graph* const graph_; Graph* const graph_;
IdentityMap<int, base::DefaultAllocationPolicy> deopt_literals_;
int deopt_exit_start_offset_ = -1; int deopt_exit_start_offset_ = -1;
}; };
......
...@@ -13,9 +13,11 @@ namespace v8 { ...@@ -13,9 +13,11 @@ namespace v8 {
namespace internal { namespace internal {
namespace maglev { namespace maglev {
MaglevCompilationUnit::MaglevCompilationUnit(MaglevCompilationInfo* info, MaglevCompilationUnit::MaglevCompilationUnit(
Handle<JSFunction> function) MaglevCompilationInfo* info, const MaglevCompilationUnit* caller,
Handle<JSFunction> function)
: info_(info), : info_(info),
caller_(caller),
function_(MakeRef(broker(), function)), function_(MakeRef(broker(), function)),
shared_function_info_(function_.shared()), shared_function_info_(function_.shared()),
bytecode_(shared_function_info_.GetBytecodeArray()), bytecode_(shared_function_info_.GetBytecodeArray()),
...@@ -24,7 +26,8 @@ MaglevCompilationUnit::MaglevCompilationUnit(MaglevCompilationInfo* info, ...@@ -24,7 +26,8 @@ MaglevCompilationUnit::MaglevCompilationUnit(MaglevCompilationInfo* info,
bytecode_analysis_(bytecode_.object(), zone(), BytecodeOffset::None(), bytecode_analysis_(bytecode_.object(), zone(), BytecodeOffset::None(),
true), true),
register_count_(bytecode_.register_count()), register_count_(bytecode_.register_count()),
parameter_count_(bytecode_.parameter_count()) {} parameter_count_(bytecode_.parameter_count()),
inlining_depth_(caller == nullptr ? 0 : caller->inlining_depth_ + 1) {}
compiler::JSHeapBroker* MaglevCompilationUnit::broker() const { compiler::JSHeapBroker* MaglevCompilationUnit::broker() const {
return info_->broker(); return info_->broker();
......
...@@ -24,18 +24,27 @@ class MaglevCompilationUnit : public ZoneObject { ...@@ -24,18 +24,27 @@ class MaglevCompilationUnit : public ZoneObject {
public: public:
static MaglevCompilationUnit* New(Zone* zone, MaglevCompilationInfo* info, static MaglevCompilationUnit* New(Zone* zone, MaglevCompilationInfo* info,
Handle<JSFunction> function) { Handle<JSFunction> function) {
return zone->New<MaglevCompilationUnit>(info, function); return zone->New<MaglevCompilationUnit>(info, nullptr, function);
} }
static MaglevCompilationUnit* NewInner(Zone* zone,
const MaglevCompilationUnit* caller,
Handle<JSFunction> function) {
return zone->New<MaglevCompilationUnit>(caller->info(), caller, function);
}
MaglevCompilationUnit(MaglevCompilationInfo* info, MaglevCompilationUnit(MaglevCompilationInfo* info,
const MaglevCompilationUnit* caller,
Handle<JSFunction> function); Handle<JSFunction> function);
MaglevCompilationInfo* info() const { return info_; } MaglevCompilationInfo* info() const { return info_; }
const MaglevCompilationUnit* caller() const { return caller_; }
compiler::JSHeapBroker* broker() const; compiler::JSHeapBroker* broker() const;
Isolate* isolate() const; Isolate* isolate() const;
LocalIsolate* local_isolate() const; LocalIsolate* local_isolate() const;
Zone* zone() const; Zone* zone() const;
int register_count() const { return register_count_; } int register_count() const { return register_count_; }
int parameter_count() const { return parameter_count_; } int parameter_count() const { return parameter_count_; }
int inlining_depth() const { return inlining_depth_; }
bool has_graph_labeller() const; bool has_graph_labeller() const;
MaglevGraphLabeller* graph_labeller() const; MaglevGraphLabeller* graph_labeller() const;
const compiler::SharedFunctionInfoRef& shared_function_info() const { const compiler::SharedFunctionInfoRef& shared_function_info() const {
...@@ -52,6 +61,7 @@ class MaglevCompilationUnit : public ZoneObject { ...@@ -52,6 +61,7 @@ class MaglevCompilationUnit : public ZoneObject {
private: private:
MaglevCompilationInfo* const info_; MaglevCompilationInfo* const info_;
const MaglevCompilationUnit* const caller_;
const compiler::JSFunctionRef function_; const compiler::JSFunctionRef function_;
const compiler::SharedFunctionInfoRef shared_function_info_; const compiler::SharedFunctionInfoRef shared_function_info_;
const compiler::BytecodeArrayRef bytecode_; const compiler::BytecodeArrayRef bytecode_;
...@@ -59,6 +69,7 @@ class MaglevCompilationUnit : public ZoneObject { ...@@ -59,6 +69,7 @@ class MaglevCompilationUnit : public ZoneObject {
const compiler::BytecodeAnalysis bytecode_analysis_; const compiler::BytecodeAnalysis bytecode_analysis_;
const int register_count_; const int register_count_;
const int parameter_count_; const int parameter_count_;
const int inlining_depth_;
}; };
} // namespace maglev } // namespace maglev
......
...@@ -108,31 +108,39 @@ class UseMarkingProcessor { ...@@ -108,31 +108,39 @@ class UseMarkingProcessor {
} }
private: private:
void MarkCheckpointNodes(NodeBase* node, const EagerDeoptInfo* deopt_info, void MarkCheckpointNodes(NodeBase* node, const MaglevCompilationUnit& unit,
const ProcessingState& state) { const CheckpointedInterpreterState* checkpoint_state,
const MaglevCompilationUnit& compilation_unit = InputLocation* input_locations,
*state.compilation_info()->toplevel_compilation_unit(); const ProcessingState& state, int& index) {
if (checkpoint_state->parent) {
MarkCheckpointNodes(node, *unit.caller(), checkpoint_state->parent,
input_locations, state, index);
}
const CompactInterpreterFrameState* register_frame = const CompactInterpreterFrameState* register_frame =
deopt_info->state.register_frame; checkpoint_state->register_frame;
int use_id = node->id(); int use_id = node->id();
int index = 0;
register_frame->ForEachValue( register_frame->ForEachValue(
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { unit, [&](ValueNode* node, interpreter::Register reg) {
node->mark_use(use_id, &deopt_info->input_locations[index++]); node->mark_use(use_id, &input_locations[index++]);
}); });
} }
void MarkCheckpointNodes(NodeBase* node, const EagerDeoptInfo* deopt_info,
const ProcessingState& state) {
int index = 0;
MarkCheckpointNodes(node, deopt_info->unit, &deopt_info->state,
deopt_info->input_locations, state, index);
}
void MarkCheckpointNodes(NodeBase* node, const LazyDeoptInfo* deopt_info, void MarkCheckpointNodes(NodeBase* node, const LazyDeoptInfo* deopt_info,
const ProcessingState& state) { const ProcessingState& state) {
const MaglevCompilationUnit& compilation_unit =
*state.compilation_info()->toplevel_compilation_unit();
const CompactInterpreterFrameState* register_frame = const CompactInterpreterFrameState* register_frame =
deopt_info->state.register_frame; deopt_info->state.register_frame;
int use_id = node->id(); int use_id = node->id();
int index = 0; int index = 0;
register_frame->ForEachValue( register_frame->ForEachValue(
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { deopt_info->unit, [&](ValueNode* node, interpreter::Register reg) {
// Skip over the result location. // Skip over the result location.
if (reg == deopt_info->result_location) return; if (reg == deopt_info->result_location) return;
node->mark_use(use_id, &deopt_info->input_locations[index++]); node->mark_use(use_id, &deopt_info->input_locations[index++]);
......
This diff is collapsed.
...@@ -27,9 +27,28 @@ class MaglevGraphBuilder { ...@@ -27,9 +27,28 @@ class MaglevGraphBuilder {
public: public:
explicit MaglevGraphBuilder(LocalIsolate* local_isolate, explicit MaglevGraphBuilder(LocalIsolate* local_isolate,
MaglevCompilationUnit* compilation_unit, MaglevCompilationUnit* compilation_unit,
Graph* graph); Graph* graph,
MaglevGraphBuilder* parent = nullptr);
void Build() { void Build() {
DCHECK(!is_inline());
StartPrologue();
for (int i = 0; i < parameter_count(); i++) {
SetArgument(i, AddNewNode<InitialValue>(
{}, interpreter::Register::FromParameterIndex(i)));
}
BuildRegisterFrameInitialization();
EndPrologue();
BuildBody();
}
void StartPrologue();
void SetArgument(int i, ValueNode* value);
void BuildRegisterFrameInitialization();
BasicBlock* EndPrologue();
void BuildBody() {
for (iterator_.Reset(); !iterator_.done(); iterator_.Advance()) { for (iterator_.Reset(); !iterator_.done(); iterator_.Advance()) {
VisitSingleBytecode(); VisitSingleBytecode();
// TODO(v8:7700): Clean up after all bytecodes are supported. // TODO(v8:7700): Clean up after all bytecodes are supported.
...@@ -343,7 +362,13 @@ class MaglevGraphBuilder { ...@@ -343,7 +362,13 @@ class MaglevGraphBuilder {
latest_checkpointed_state_.emplace( latest_checkpointed_state_.emplace(
BytecodeOffset(iterator_.current_offset()), BytecodeOffset(iterator_.current_offset()),
zone()->New<CompactInterpreterFrameState>( zone()->New<CompactInterpreterFrameState>(
*compilation_unit_, GetInLiveness(), current_interpreter_frame_)); *compilation_unit_, GetInLiveness(), current_interpreter_frame_),
parent_ == nullptr
? nullptr
// TODO(leszeks): Don't always allocate for the parent state,
// maybe cache it on the graph builder?
: zone()->New<CheckpointedInterpreterState>(
parent_->GetLatestCheckpointedState()));
} }
return *latest_checkpointed_state_; return *latest_checkpointed_state_;
} }
...@@ -352,7 +377,9 @@ class MaglevGraphBuilder { ...@@ -352,7 +377,9 @@ class MaglevGraphBuilder {
return CheckpointedInterpreterState( return CheckpointedInterpreterState(
BytecodeOffset(iterator_.current_offset()), BytecodeOffset(iterator_.current_offset()),
zone()->New<CompactInterpreterFrameState>( zone()->New<CompactInterpreterFrameState>(
*compilation_unit_, GetOutLiveness(), current_interpreter_frame_)); *compilation_unit_, GetOutLiveness(), current_interpreter_frame_),
// TODO(leszeks): Support lazy deopts in inlined functions.
nullptr);
} }
template <typename NodeT> template <typename NodeT>
...@@ -444,6 +471,10 @@ class MaglevGraphBuilder { ...@@ -444,6 +471,10 @@ class MaglevGraphBuilder {
return block; return block;
} }
void InlineCallFromRegisters(int argc_count,
ConvertReceiverMode receiver_mode,
compiler::JSFunctionRef function);
void BuildCallFromRegisterList(ConvertReceiverMode receiver_mode); void BuildCallFromRegisterList(ConvertReceiverMode receiver_mode);
void BuildCallFromRegisters(int argc_count, void BuildCallFromRegisters(int argc_count,
ConvertReceiverMode receiver_mode); ConvertReceiverMode receiver_mode);
...@@ -465,6 +496,7 @@ class MaglevGraphBuilder { ...@@ -465,6 +496,7 @@ class MaglevGraphBuilder {
void VisitBinarySmiOperation(); void VisitBinarySmiOperation();
void MergeIntoFrameState(BasicBlock* block, int target); void MergeIntoFrameState(BasicBlock* block, int target);
void MergeIntoInlinedReturnFrameState(BasicBlock* block);
void BuildBranchIfTrue(ValueNode* node, int true_target, int false_target); void BuildBranchIfTrue(ValueNode* node, int true_target, int false_target);
void BuildBranchIfToBooleanTrue(ValueNode* node, int true_target, void BuildBranchIfToBooleanTrue(ValueNode* node, int true_target,
int false_target); int false_target);
...@@ -491,11 +523,17 @@ class MaglevGraphBuilder { ...@@ -491,11 +523,17 @@ class MaglevGraphBuilder {
} else if (interpreter::Bytecodes::Returns(bytecode) || } else if (interpreter::Bytecodes::Returns(bytecode) ||
interpreter::Bytecodes::UnconditionallyThrows(bytecode)) { interpreter::Bytecodes::UnconditionallyThrows(bytecode)) {
predecessors_[iterator.next_offset()]--; predecessors_[iterator.next_offset()]--;
// Collect inline return jumps in the slot after the last bytecode.
if (is_inline() && interpreter::Bytecodes::Returns(bytecode)) {
predecessors_[array_length - 1]++;
}
} }
// TODO(leszeks): Also consider handler entries (the bytecode analysis) // TODO(leszeks): Also consider handler entries (the bytecode analysis)
// will do this automatically I guess if we merge this into that. // will do this automatically I guess if we merge this into that.
} }
DCHECK_EQ(0, predecessors_[bytecode().length()]); if (!is_inline()) {
DCHECK_EQ(0, predecessors_[bytecode().length()]);
}
} }
int NumPredecessors(int offset) { return predecessors_[offset]; } int NumPredecessors(int offset) { return predecessors_[offset]; }
...@@ -530,8 +568,19 @@ class MaglevGraphBuilder { ...@@ -530,8 +568,19 @@ class MaglevGraphBuilder {
return compilation_unit_->graph_labeller(); return compilation_unit_->graph_labeller();
} }
// True when this graph builder is building the subgraph of an inlined
// function.
bool is_inline() const { return parent_ != nullptr; }
// The fake offset used as a target for all exits of an inlined function.
int inline_exit_offset() const {
DCHECK(is_inline());
return bytecode().length();
}
LocalIsolate* const local_isolate_; LocalIsolate* const local_isolate_;
MaglevCompilationUnit* const compilation_unit_; MaglevCompilationUnit* const compilation_unit_;
MaglevGraphBuilder* const parent_;
Graph* const graph_; Graph* const graph_;
interpreter::BytecodeArrayIterator iterator_; interpreter::BytecodeArrayIterator iterator_;
uint32_t* predecessors_; uint32_t* predecessors_;
......
...@@ -314,10 +314,8 @@ void PrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets, ...@@ -314,10 +314,8 @@ void PrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
os << " ↱ eager @" << deopt_info->state.bytecode_position << " : {"; os << " ↱ eager @" << deopt_info->state.bytecode_position << " : {";
bool first = true; bool first = true;
int index = 0; int index = 0;
const MaglevCompilationUnit& compilation_unit =
*state.compilation_info()->toplevel_compilation_unit();
deopt_info->state.register_frame->ForEachValue( deopt_info->state.register_frame->ForEachValue(
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { deopt_info->unit, [&](ValueNode* node, interpreter::Register reg) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
...@@ -355,10 +353,8 @@ void PrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets, ...@@ -355,10 +353,8 @@ void PrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
os << " ↳ lazy @" << deopt_info->state.bytecode_position << " : {"; os << " ↳ lazy @" << deopt_info->state.bytecode_position << " : {";
bool first = true; bool first = true;
int index = 0; int index = 0;
const MaglevCompilationUnit& compilation_unit =
*state.compilation_info()->toplevel_compilation_unit();
deopt_info->state.register_frame->ForEachValue( deopt_info->state.register_frame->ForEachValue(
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { deopt_info->unit, [&](ValueNode* node, interpreter::Register reg) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
......
...@@ -70,6 +70,8 @@ class MaglevGraphVerifier { ...@@ -70,6 +70,8 @@ class MaglevGraphVerifier {
case Opcode::kDeopt: case Opcode::kDeopt:
case Opcode::kJump: case Opcode::kJump:
case Opcode::kJumpLoop: case Opcode::kJumpLoop:
case Opcode::kJumpToInlined:
case Opcode::kJumpFromInlined:
// No input. // No input.
DCHECK_EQ(node->input_count(), 0); DCHECK_EQ(node->input_count(), 0);
break; break;
......
...@@ -336,15 +336,32 @@ void NodeBase::Print(std::ostream& os, ...@@ -336,15 +336,32 @@ void NodeBase::Print(std::ostream& os,
UNREACHABLE(); UNREACHABLE();
} }
namespace {
size_t GetInputLocationsArraySize(const MaglevCompilationUnit& compilation_unit,
const CheckpointedInterpreterState& state) {
size_t size = state.register_frame->size(compilation_unit);
const CheckpointedInterpreterState* parent = state.parent;
const MaglevCompilationUnit* parent_unit = compilation_unit.caller();
while (parent != nullptr) {
size += parent->register_frame->size(*parent_unit);
parent = parent->parent;
parent_unit = parent_unit->caller();
}
return size;
}
} // namespace
DeoptInfo::DeoptInfo(Zone* zone, const MaglevCompilationUnit& compilation_unit, DeoptInfo::DeoptInfo(Zone* zone, const MaglevCompilationUnit& compilation_unit,
CheckpointedInterpreterState state) CheckpointedInterpreterState state)
: state(state), : unit(compilation_unit),
state(state),
input_locations(zone->NewArray<InputLocation>( input_locations(zone->NewArray<InputLocation>(
state.register_frame->size(compilation_unit))) { GetInputLocationsArraySize(compilation_unit, state))) {
// Default initialise if we're printing the graph, to avoid printing junk // Default initialise if we're printing the graph, to avoid printing junk
// values. // values.
if (FLAG_print_maglev_graph) { if (FLAG_print_maglev_graph) {
for (size_t i = 0; i < state.register_frame->size(compilation_unit); ++i) { for (size_t i = 0; i < GetInputLocationsArraySize(compilation_unit, state);
++i) {
new (&input_locations[i]) InputLocation(); new (&input_locations[i]) InputLocation();
} }
} }
...@@ -894,6 +911,30 @@ void Jump::GenerateCode(MaglevCodeGenState* code_gen_state, ...@@ -894,6 +911,30 @@ void Jump::GenerateCode(MaglevCodeGenState* code_gen_state,
} }
} }
void JumpToInlined::AllocateVreg(MaglevVregAllocationState* vreg_state,
const ProcessingState& state) {}
void JumpToInlined::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
// Avoid emitting a jump to the next block.
if (target() != state.next_block()) {
__ jmp(target()->label());
}
}
void JumpToInlined::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << Brief(*unit()->shared_function_info().object()) << ")";
}
void JumpFromInlined::AllocateVreg(MaglevVregAllocationState* vreg_state,
const ProcessingState& state) {}
void JumpFromInlined::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
// Avoid emitting a jump to the next block.
if (target() != state.next_block()) {
__ jmp(target()->label());
}
}
void JumpLoop::AllocateVreg(MaglevVregAllocationState* vreg_state, void JumpLoop::AllocateVreg(MaglevVregAllocationState* vreg_state,
const ProcessingState& state) {} const ProcessingState& state) {}
void JumpLoop::GenerateCode(MaglevCodeGenState* code_gen_state, void JumpLoop::GenerateCode(MaglevCodeGenState* code_gen_state,
......
...@@ -94,7 +94,9 @@ class CompactInterpreterFrameState; ...@@ -94,7 +94,9 @@ class CompactInterpreterFrameState;
#define UNCONDITIONAL_CONTROL_NODE_LIST(V) \ #define UNCONDITIONAL_CONTROL_NODE_LIST(V) \
V(Jump) \ V(Jump) \
V(JumpLoop) V(JumpLoop) \
V(JumpToInlined) \
V(JumpFromInlined)
#define CONTROL_NODE_LIST(V) \ #define CONTROL_NODE_LIST(V) \
V(Return) \ V(Return) \
...@@ -328,11 +330,15 @@ class CheckpointedInterpreterState { ...@@ -328,11 +330,15 @@ class CheckpointedInterpreterState {
public: public:
CheckpointedInterpreterState() = default; CheckpointedInterpreterState() = default;
CheckpointedInterpreterState(BytecodeOffset bytecode_position, CheckpointedInterpreterState(BytecodeOffset bytecode_position,
const CompactInterpreterFrameState* state) const CompactInterpreterFrameState* state,
: bytecode_position(bytecode_position), register_frame(state) {} const CheckpointedInterpreterState* parent)
: bytecode_position(bytecode_position),
register_frame(state),
parent(parent) {}
BytecodeOffset bytecode_position = BytecodeOffset::None(); BytecodeOffset bytecode_position = BytecodeOffset::None();
const CompactInterpreterFrameState* register_frame = nullptr; const CompactInterpreterFrameState* register_frame = nullptr;
const CheckpointedInterpreterState* parent = nullptr;
}; };
class DeoptInfo { class DeoptInfo {
...@@ -341,6 +347,7 @@ class DeoptInfo { ...@@ -341,6 +347,7 @@ class DeoptInfo {
CheckpointedInterpreterState checkpoint); CheckpointedInterpreterState checkpoint);
public: public:
const MaglevCompilationUnit& unit;
CheckpointedInterpreterState state; CheckpointedInterpreterState state;
InputLocation* input_locations = nullptr; InputLocation* input_locations = nullptr;
Label deopt_entry_label; Label deopt_entry_label;
...@@ -1464,9 +1471,9 @@ class ControlNode : public NodeBase { ...@@ -1464,9 +1471,9 @@ class ControlNode : public NodeBase {
return next_post_dominating_hole_; return next_post_dominating_hole_;
} }
void set_next_post_dominating_hole(ControlNode* node) { void set_next_post_dominating_hole(ControlNode* node) {
DCHECK_IMPLIES(node != nullptr, node->Is<Jump>() || node->Is<Return>() || DCHECK_IMPLIES(node != nullptr, node->Is<UnconditionalControlNode>() ||
node->Is<Deopt>() || node->Is<Return>() ||
node->Is<JumpLoop>()); node->Is<Deopt>());
next_post_dominating_hole_ = node; next_post_dominating_hole_ = node;
} }
...@@ -1590,6 +1597,36 @@ class JumpLoop : public UnconditionalControlNodeT<JumpLoop> { ...@@ -1590,6 +1597,36 @@ class JumpLoop : public UnconditionalControlNodeT<JumpLoop> {
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {} void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
}; };
class JumpToInlined : public UnconditionalControlNodeT<JumpToInlined> {
using Base = UnconditionalControlNodeT<JumpToInlined>;
public:
explicit JumpToInlined(uint32_t bitfield, BasicBlockRef* target_refs,
MaglevCompilationUnit* unit)
: Base(bitfield, target_refs), unit_(unit) {}
void AllocateVreg(MaglevVregAllocationState*, const ProcessingState&);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const;
const MaglevCompilationUnit* unit() const { return unit_; }
private:
MaglevCompilationUnit* unit_;
};
class JumpFromInlined : public UnconditionalControlNodeT<JumpFromInlined> {
using Base = UnconditionalControlNodeT<JumpFromInlined>;
public:
explicit JumpFromInlined(uint32_t bitfield, BasicBlockRef* target_refs)
: Base(bitfield, target_refs) {}
void AllocateVreg(MaglevVregAllocationState*, const ProcessingState&);
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
};
class Return : public ControlNode { class Return : public ControlNode {
public: public:
explicit Return(uint32_t bitfield) : ControlNode(bitfield) { explicit Return(uint32_t bitfield) : ControlNode(bitfield) {
......
...@@ -339,28 +339,18 @@ void StraightForwardRegisterAllocator::UpdateUse( ...@@ -339,28 +339,18 @@ void StraightForwardRegisterAllocator::UpdateUse(
void StraightForwardRegisterAllocator::UpdateUse( void StraightForwardRegisterAllocator::UpdateUse(
const EagerDeoptInfo& deopt_info) { const EagerDeoptInfo& deopt_info) {
const CompactInterpreterFrameState* checkpoint_state =
deopt_info.state.register_frame;
const MaglevCompilationUnit& compilation_unit =
*compilation_info_->toplevel_compilation_unit();
int index = 0; int index = 0;
checkpoint_state->ForEachValue( UpdateUse(deopt_info.unit, &deopt_info.state, deopt_info.input_locations,
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { index);
InputLocation* input = &deopt_info.input_locations[index++];
input->InjectAllocated(node->allocation());
UpdateUse(node, input);
});
} }
void StraightForwardRegisterAllocator::UpdateUse( void StraightForwardRegisterAllocator::UpdateUse(
const LazyDeoptInfo& deopt_info) { const LazyDeoptInfo& deopt_info) {
const CompactInterpreterFrameState* checkpoint_state = const CompactInterpreterFrameState* checkpoint_state =
deopt_info.state.register_frame; deopt_info.state.register_frame;
const MaglevCompilationUnit& compilation_unit =
*compilation_info_->toplevel_compilation_unit();
int index = 0; int index = 0;
checkpoint_state->ForEachValue( checkpoint_state->ForEachValue(
compilation_unit, [&](ValueNode* node, interpreter::Register reg) { deopt_info.unit, [&](ValueNode* node, interpreter::Register reg) {
// Skip over the result location. // Skip over the result location.
if (reg == deopt_info.result_location) return; if (reg == deopt_info.result_location) return;
InputLocation* input = &deopt_info.input_locations[index++]; InputLocation* input = &deopt_info.input_locations[index++];
...@@ -369,6 +359,22 @@ void StraightForwardRegisterAllocator::UpdateUse( ...@@ -369,6 +359,22 @@ void StraightForwardRegisterAllocator::UpdateUse(
}); });
} }
void StraightForwardRegisterAllocator::UpdateUse(
const MaglevCompilationUnit& unit,
const CheckpointedInterpreterState* state, InputLocation* input_locations,
int& index) {
if (state->parent) {
UpdateUse(*unit.caller(), state->parent, input_locations, index);
}
const CompactInterpreterFrameState* checkpoint_state = state->register_frame;
checkpoint_state->ForEachValue(
unit, [&](ValueNode* node, interpreter::Register reg) {
InputLocation* input = &input_locations[index++];
input->InjectAllocated(node->allocation());
UpdateUse(node, input);
});
}
void StraightForwardRegisterAllocator::AllocateNode(Node* node) { void StraightForwardRegisterAllocator::AllocateNode(Node* node) {
for (Input& input : *node) AssignInput(input); for (Input& input : *node) AssignInput(input);
AssignTemporaries(node); AssignTemporaries(node);
...@@ -546,7 +552,10 @@ void StraightForwardRegisterAllocator::AllocateControlNode(ControlNode* node, ...@@ -546,7 +552,10 @@ void StraightForwardRegisterAllocator::AllocateControlNode(ControlNode* node,
// Merge register values. Values only flowing into phis and not being // Merge register values. Values only flowing into phis and not being
// independently live will be killed as part of the merge. // independently live will be killed as part of the merge.
if (auto unconditional = node->TryCast<UnconditionalControlNode>()) { if (node->Is<JumpToInlined>()) {
// Do nothing.
// TODO(leszeks): DCHECK any useful invariants here.
} else if (auto unconditional = node->TryCast<UnconditionalControlNode>()) {
// Empty blocks are immediately merged at the control of their predecessor. // Empty blocks are immediately merged at the control of their predecessor.
if (!block->is_empty_block()) { if (!block->is_empty_block()) {
MergeRegisterValues(unconditional, unconditional->target(), MergeRegisterValues(unconditional, unconditional->target(),
......
...@@ -62,6 +62,9 @@ class StraightForwardRegisterAllocator { ...@@ -62,6 +62,9 @@ class StraightForwardRegisterAllocator {
void UpdateUse(ValueNode* node, InputLocation* input_location); void UpdateUse(ValueNode* node, InputLocation* input_location);
void UpdateUse(const EagerDeoptInfo& deopt_info); void UpdateUse(const EagerDeoptInfo& deopt_info);
void UpdateUse(const LazyDeoptInfo& deopt_info); void UpdateUse(const LazyDeoptInfo& deopt_info);
void UpdateUse(const MaglevCompilationUnit& unit,
const CheckpointedInterpreterState* state,
InputLocation* input_locations, int& index);
void AllocateControlNode(ControlNode* node, BasicBlock* block); void AllocateControlNode(ControlNode* node, BasicBlock* block);
void AllocateNode(Node* node); void AllocateNode(Node* node);
......
// Copyright 2022 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 --maglev --maglev-inlining --no-stress-opt
function inner(o) {
"use strict"
return 10 + o.x + 100;
}
function foo(o) {
return 1000 + inner(o) + 10000;
}
%PrepareFunctionForOptimization(inner);
%PrepareFunctionForOptimization(foo);
assertEquals(11111, foo({x:1}));
assertEquals(11111, foo({x:1}));
%OptimizeMaglevOnNextCall(foo);
// The inlined inner function will deopt -- this deopt should succeed.
assertEquals(11111, foo({y:2,x:1}));
// Copyright 2022 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 --maglev --maglev-inlining --no-stress-opt
function global_func(x) {
return x;
}
function foo(x) {
return global_func(x);
}
%PrepareFunctionForOptimization(foo);
%PrepareFunctionForOptimization(global_func);
print(foo(1));
print(foo(1));
%OptimizeMaglevOnNextCall(foo);
print(foo(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