[maglev] Implement Maglev-to-Turbofan OSR
This implementation sticks closely to what Ignition-to-Turbofan (and now Sparkplug-to-TF) does. OSR is detected in the TieringManager by having optimized code available, without having entered it. The osr_urgency is increased to enable OSR for increasing loop depths. When a candidate JumpLoop backedge is reached, we call into runtime to trigger OSR compilation. JumpLoop also detects the availability of cached OSR'd code. When a matching OSR code object is available, Maglev 1) deoptimizes s.t. the unoptimized frame layout is reconstructed, and 2) delegates the actual OSR tierup to the unoptimized tier. For purposes of 1), we add a new DeoptimizeReason that causes a one-time eager deopt without invalidating any code. Drive-by: Annotate OSR for more --trace-opt output. Todo: Refactor non-Sparkplug-specific bits of the BaselineAssembler into a generic spot that both SP and ML can use. Bug: v8:7700 Change-Id: I6ebab2df8b87f9f70ffb78162a3c1226ec545468 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3859850 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Jakob Linke <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#82816}
This commit is contained in:
parent
8e069d6294
commit
ed90ea5cf7
1
BUILD.gn
1
BUILD.gn
@ -3623,6 +3623,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/maglev/maglev-graph-verifier.h",
|
||||
"src/maglev/maglev-graph.h",
|
||||
"src/maglev/maglev-interpreter-frame-state.h",
|
||||
"src/maglev/maglev-ir-inl.h",
|
||||
"src/maglev/maglev-ir.h",
|
||||
"src/maglev/maglev-regalloc-data.h",
|
||||
"src/maglev/maglev-regalloc.h",
|
||||
|
@ -1900,7 +1900,7 @@ void BaselineCompiler::VisitCreateRestParameter() {
|
||||
|
||||
void BaselineCompiler::VisitJumpLoop() {
|
||||
Label osr_armed, osr_not_armed;
|
||||
using D = BaselineOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
Register feedback_vector = Register::no_reg();
|
||||
Register osr_state = Register::no_reg();
|
||||
const int loop_depth = iterator().GetImmediateOperand(1);
|
||||
|
@ -1780,14 +1780,14 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = InterpreterOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
OnStackReplacement(masm, OsrSourceTier::kInterpreter,
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = BaselineOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
|
||||
__ ldr(kContextRegister,
|
||||
|
@ -2018,14 +2018,14 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = InterpreterOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
OnStackReplacement(masm, OsrSourceTier::kInterpreter,
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = BaselineOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
|
||||
__ ldr(kContextRegister,
|
||||
|
@ -185,17 +185,20 @@ namespace internal {
|
||||
InterpreterPushArgsThenConstruct) \
|
||||
ASM(InterpreterEnterAtBytecode, Void) \
|
||||
ASM(InterpreterEnterAtNextBytecode, Void) \
|
||||
ASM(InterpreterOnStackReplacement, InterpreterOnStackReplacement) \
|
||||
ASM(InterpreterOnStackReplacement, OnStackReplacement) \
|
||||
\
|
||||
/* Baseline Compiler */ \
|
||||
ASM(BaselineOutOfLinePrologue, BaselineOutOfLinePrologue) \
|
||||
ASM(BaselineOutOfLinePrologueDeopt, Void) \
|
||||
ASM(BaselineOnStackReplacement, BaselineOnStackReplacement) \
|
||||
ASM(BaselineOnStackReplacement, OnStackReplacement) \
|
||||
ASM(BaselineLeaveFrame, BaselineLeaveFrame) \
|
||||
ASM(BaselineOrInterpreterEnterAtBytecode, Void) \
|
||||
ASM(BaselineOrInterpreterEnterAtNextBytecode, Void) \
|
||||
ASM(InterpreterOnStackReplacement_ToBaseline, Void) \
|
||||
\
|
||||
/* Maglev Compiler */ \
|
||||
ASM(MaglevOnStackReplacement, OnStackReplacement) \
|
||||
\
|
||||
/* Code life-cycle */ \
|
||||
TFC(CompileLazy, JSTrampoline) \
|
||||
TFC(CompileLazyDeoptimizedCode, JSTrampoline) \
|
||||
|
@ -1300,6 +1300,16 @@ void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO(v8:11421): Remove #if once the Maglev compiler is ported to other
|
||||
// architectures.
|
||||
#ifndef V8_TARGET_ARCH_X64
|
||||
void Builtins::Generate_MaglevOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
masm->Trap();
|
||||
}
|
||||
#endif // V8_TARGET_ARCH_X64
|
||||
|
||||
// ES6 [[Get]] operation.
|
||||
TF_BUILTIN(GetProperty, CodeStubAssembler) {
|
||||
auto object = Parameter<Object>(Descriptor::kObject);
|
||||
|
@ -2750,14 +2750,14 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = InterpreterOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
OnStackReplacement(masm, OsrSourceTier::kInterpreter,
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = BaselineOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
|
||||
__ mov(kContextRegister,
|
||||
|
@ -2599,6 +2599,7 @@ void Generate_OSREntry(MacroAssembler* masm, Register entry_address) {
|
||||
enum class OsrSourceTier {
|
||||
kInterpreter,
|
||||
kBaseline,
|
||||
kMaglev,
|
||||
};
|
||||
|
||||
void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
@ -2625,6 +2626,14 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
__ bind(&jump_to_optimized_code);
|
||||
DCHECK_EQ(maybe_target_code, rax); // Already in the right spot.
|
||||
|
||||
if (source == OsrSourceTier::kMaglev) {
|
||||
// Maglev doesn't enter OSR'd code itself, since OSR depends on the
|
||||
// unoptimized (~= Ignition) stack frame layout. Instead, return to Maglev
|
||||
// code and let it deoptimize.
|
||||
__ ret(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// OSR entry tracing.
|
||||
{
|
||||
Label next;
|
||||
@ -2646,7 +2655,7 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
|
||||
if (source == OsrSourceTier::kInterpreter) {
|
||||
// Drop the handler frame that is be sitting on top of the actual
|
||||
// JavaScript frame. This is the case then OSR is triggered from bytecode.
|
||||
// JavaScript frame.
|
||||
__ leave();
|
||||
}
|
||||
|
||||
@ -2675,14 +2684,14 @@ void OnStackReplacement(MacroAssembler* masm, OsrSourceTier source,
|
||||
} // namespace
|
||||
|
||||
void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = InterpreterOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
OnStackReplacement(masm, OsrSourceTier::kInterpreter,
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = BaselineOnStackReplacementDescriptor;
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
__ movq(kContextRegister,
|
||||
MemOperand(rbp, BaselineFrameConstants::kContextOffset));
|
||||
@ -2690,6 +2699,13 @@ void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) {
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
void Builtins::Generate_MaglevOnStackReplacement(MacroAssembler* masm) {
|
||||
using D = OnStackReplacementDescriptor;
|
||||
static_assert(D::kParameterCount == 1);
|
||||
OnStackReplacement(masm, OsrSourceTier::kMaglev,
|
||||
D::MaybeTargetCodeRegister());
|
||||
}
|
||||
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
|
||||
// The function index was pushed to the stack by the caller as int32.
|
||||
|
@ -183,6 +183,7 @@ class CompilerTracer : public AllStatic {
|
||||
if (!FLAG_trace_opt || !info->IsOptimizing()) return;
|
||||
CodeTracer::Scope scope(isolate->GetCodeTracer());
|
||||
PrintTracePrefix(scope, "optimizing", info);
|
||||
if (info->is_osr()) PrintF(scope.file(), " OSR");
|
||||
PrintF(scope.file(), " - took %0.3f, %0.3f, %0.3f ms", ms_creategraph,
|
||||
ms_optimize, ms_codegen);
|
||||
PrintTraceSuffix(scope);
|
||||
@ -215,6 +216,7 @@ class CompilerTracer : public AllStatic {
|
||||
if (!FLAG_trace_opt) return;
|
||||
CodeTracer::Scope scope(isolate->GetCodeTracer());
|
||||
PrintTracePrefix(scope, "completed optimizing", info);
|
||||
if (info->is_osr()) PrintF(scope.file(), " OSR");
|
||||
PrintTraceSuffix(scope);
|
||||
}
|
||||
|
||||
@ -223,6 +225,7 @@ class CompilerTracer : public AllStatic {
|
||||
if (!FLAG_trace_opt) return;
|
||||
CodeTracer::Scope scope(isolate->GetCodeTracer());
|
||||
PrintTracePrefix(scope, "aborted optimizing", info);
|
||||
if (info->is_osr()) PrintF(scope.file(), " OSR");
|
||||
PrintF(scope.file(), " because: %s",
|
||||
GetBailoutReason(info->bailout_reason()));
|
||||
PrintTraceSuffix(scope);
|
||||
|
@ -365,31 +365,17 @@ constexpr auto BaselineLeaveFrameDescriptor::registers() {
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr auto BaselineOnStackReplacementDescriptor::registers() {
|
||||
constexpr auto OnStackReplacementDescriptor::registers() {
|
||||
return DefaultRegisterArray();
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr Register
|
||||
BaselineOnStackReplacementDescriptor::MaybeTargetCodeRegister() {
|
||||
constexpr Register OnStackReplacementDescriptor::MaybeTargetCodeRegister() {
|
||||
// Picking the first register on purpose because it's convenient that this
|
||||
// register is the same as the platform's return-value register.
|
||||
return registers()[0];
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr auto InterpreterOnStackReplacementDescriptor::registers() {
|
||||
using BaselineD = BaselineOnStackReplacementDescriptor;
|
||||
return BaselineD::registers();
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr Register
|
||||
InterpreterOnStackReplacementDescriptor::MaybeTargetCodeRegister() {
|
||||
using BaselineD = BaselineOnStackReplacementDescriptor;
|
||||
return BaselineD::MaybeTargetCodeRegister();
|
||||
}
|
||||
|
||||
// static
|
||||
constexpr auto VoidDescriptor::registers() { return RegisterArray(); }
|
||||
|
||||
|
@ -32,7 +32,6 @@ namespace internal {
|
||||
V(ArraySingleArgumentConstructor) \
|
||||
V(AsyncFunctionStackParameter) \
|
||||
V(BaselineLeaveFrame) \
|
||||
V(BaselineOnStackReplacement) \
|
||||
V(BaselineOutOfLinePrologue) \
|
||||
V(BigIntToI32Pair) \
|
||||
V(BigIntToI64) \
|
||||
@ -82,7 +81,6 @@ namespace internal {
|
||||
V(InterpreterCEntry1) \
|
||||
V(InterpreterCEntry2) \
|
||||
V(InterpreterDispatch) \
|
||||
V(InterpreterOnStackReplacement) \
|
||||
V(InterpreterPushArgsThenCall) \
|
||||
V(InterpreterPushArgsThenConstruct) \
|
||||
V(JSTrampoline) \
|
||||
@ -104,6 +102,7 @@ namespace internal {
|
||||
V(LookupBaseline) \
|
||||
V(NewHeapNumber) \
|
||||
V(NoContext) \
|
||||
V(OnStackReplacement) \
|
||||
V(RestartFrameTrampoline) \
|
||||
V(ResumeGenerator) \
|
||||
V(ResumeGeneratorBaseline) \
|
||||
@ -1722,26 +1721,12 @@ class BaselineLeaveFrameDescriptor
|
||||
static constexpr inline auto registers();
|
||||
};
|
||||
|
||||
class InterpreterOnStackReplacementDescriptor
|
||||
: public StaticCallInterfaceDescriptor<
|
||||
InterpreterOnStackReplacementDescriptor> {
|
||||
class OnStackReplacementDescriptor
|
||||
: public StaticCallInterfaceDescriptor<OnStackReplacementDescriptor> {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kMaybeTargetCode)
|
||||
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged()) // kMaybeTargetCode
|
||||
DECLARE_DESCRIPTOR(InterpreterOnStackReplacementDescriptor)
|
||||
|
||||
static constexpr inline Register MaybeTargetCodeRegister();
|
||||
|
||||
static constexpr inline auto registers();
|
||||
};
|
||||
|
||||
class BaselineOnStackReplacementDescriptor
|
||||
: public StaticCallInterfaceDescriptor<
|
||||
BaselineOnStackReplacementDescriptor> {
|
||||
public:
|
||||
DEFINE_PARAMETERS_NO_CONTEXT(kMaybeTargetCode)
|
||||
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged()) // kMaybeTargetCode
|
||||
DECLARE_DESCRIPTOR(BaselineOnStackReplacementDescriptor)
|
||||
DECLARE_DESCRIPTOR(OnStackReplacementDescriptor)
|
||||
|
||||
static constexpr inline Register MaybeTargetCodeRegister();
|
||||
|
||||
|
@ -2348,6 +2348,16 @@ void TurboAssembler::JumpCodeTObject(Register code, JumpMode jump_mode) {
|
||||
}
|
||||
}
|
||||
|
||||
void TurboAssembler::CodeDataContainerFromCodeT(Register destination,
|
||||
Register codet) {
|
||||
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
|
||||
Move(destination, codet);
|
||||
} else {
|
||||
LoadTaggedPointerField(destination,
|
||||
FieldOperand(codet, Code::kCodeDataContainerOffset));
|
||||
}
|
||||
}
|
||||
|
||||
void TurboAssembler::PextrdPreSse41(Register dst, XMMRegister src,
|
||||
uint8_t imm8) {
|
||||
if (imm8 == 0) {
|
||||
|
@ -415,6 +415,7 @@ class V8_EXPORT_PRIVATE TurboAssembler
|
||||
void LoadCodeTEntry(Register destination, Register code);
|
||||
void CallCodeTObject(Register code);
|
||||
void JumpCodeTObject(Register code, JumpMode jump_mode = JumpMode::kJump);
|
||||
void CodeDataContainerFromCodeT(Register destination, Register codet);
|
||||
|
||||
void Jump(Address destination, RelocInfo::Mode rmode);
|
||||
void Jump(const ExternalReference& reference);
|
||||
|
@ -15,6 +15,7 @@ namespace internal {
|
||||
V(BigIntTooBig, "BigInt too big") \
|
||||
V(CowArrayElementsChanged, "copy-on-write array's elements changed") \
|
||||
V(CouldNotGrowElements, "failed to grow elements store") \
|
||||
V(PrepareForOnStackReplacement, "prepare for on stack replacement (OSR)") \
|
||||
V(DeoptimizeNow, "%_DeoptimizeNow") \
|
||||
V(DivisionByZero, "division by zero") \
|
||||
V(Hole, "hole") \
|
||||
@ -91,6 +92,15 @@ size_t hash_value(DeoptimizeReason reason);
|
||||
|
||||
V8_EXPORT_PRIVATE char const* DeoptimizeReasonToString(DeoptimizeReason reason);
|
||||
|
||||
constexpr bool IsDeoptimizationWithoutCodeInvalidation(
|
||||
DeoptimizeReason reason) {
|
||||
// Maglev OSRs into Turbofan by first deoptimizing in order to restore the
|
||||
// unoptimized frame layout. Since no actual assumptions in the Maglev code
|
||||
// object are violated, it (and any associated cached optimized code) should
|
||||
// not be invalidated s.t. we may reenter it in the future.
|
||||
return reason == DeoptimizeReason::kPrepareForOnStackReplacement;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -673,8 +673,7 @@ void Deoptimizer::TraceDeoptBegin(int optimization_id,
|
||||
BytecodeOffset bytecode_offset) {
|
||||
DCHECK(tracing_enabled());
|
||||
FILE* file = trace_scope()->file();
|
||||
Deoptimizer::DeoptInfo info =
|
||||
Deoptimizer::GetDeoptInfo(compiled_code_, from_);
|
||||
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo();
|
||||
PrintF(file, "[bailout (kind: %s, reason: %s): begin. deoptimizing ",
|
||||
MessageFor(deopt_kind_), DeoptimizeReasonToString(info.deopt_reason));
|
||||
if (function_.IsJSFunction()) {
|
||||
@ -1971,8 +1970,7 @@ void Deoptimizer::MaterializeHeapObjects() {
|
||||
bool feedback_updated = translated_state_.DoUpdateFeedback();
|
||||
if (verbose_tracing_enabled() && feedback_updated) {
|
||||
FILE* file = trace_scope()->file();
|
||||
Deoptimizer::DeoptInfo info =
|
||||
Deoptimizer::GetDeoptInfo(compiled_code_, from_);
|
||||
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo();
|
||||
PrintF(file, "Feedback updated from deoptimization at ");
|
||||
OFStream outstr(file);
|
||||
info.position.Print(outstr, compiled_code_);
|
||||
|
@ -44,6 +44,9 @@ class Deoptimizer : public Malloced {
|
||||
};
|
||||
|
||||
static DeoptInfo GetDeoptInfo(Code code, Address from);
|
||||
DeoptInfo GetDeoptInfo() const {
|
||||
return Deoptimizer::GetDeoptInfo(compiled_code_, from_);
|
||||
}
|
||||
|
||||
static int ComputeSourcePositionFromBytecodeArray(
|
||||
Isolate* isolate, SharedFunctionInfo shared,
|
||||
|
@ -1525,6 +1525,17 @@ void MaglevFrame::Iterate(RootVisitor* v) const {
|
||||
IteratePc(v, pc_address(), constant_pool_address(), entry->code);
|
||||
}
|
||||
|
||||
BytecodeOffset MaglevFrame::GetBytecodeOffsetForOSR() const {
|
||||
int deopt_index = SafepointEntry::kNoDeoptIndex;
|
||||
const DeoptimizationData data = GetDeoptimizationData(&deopt_index);
|
||||
if (deopt_index == SafepointEntry::kNoDeoptIndex) {
|
||||
CHECK(data.is_null());
|
||||
FATAL("Missing deoptimization information for OptimizedFrame::Summarize.");
|
||||
}
|
||||
|
||||
return data.GetBytecodeOffset(deopt_index);
|
||||
}
|
||||
|
||||
bool CommonFrame::HasTaggedOutgoingParams(CodeLookupResult& code_lookup) const {
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
// With inlined JS-to-Wasm calls, we can be in an OptimizedFrame and
|
||||
|
@ -968,6 +968,8 @@ class MaglevFrame : public OptimizedFrame {
|
||||
|
||||
int FindReturnPCForTrampoline(CodeT code, int trampoline_pc) const override;
|
||||
|
||||
BytecodeOffset GetBytecodeOffsetForOSR() const;
|
||||
|
||||
protected:
|
||||
inline explicit MaglevFrame(StackFrameIteratorBase* iterator);
|
||||
|
||||
|
@ -263,12 +263,15 @@ void TieringManager::MaybeOptimizeFrame(JSFunction function,
|
||||
|
||||
const bool is_marked_for_any_optimization =
|
||||
(static_cast<uint32_t>(tiering_state) & kNoneOrInProgressMask) != 0;
|
||||
// Baseline OSR uses a separate mechanism and must not be considered here,
|
||||
// therefore we limit to kOptimizedJSFunctionCodeKindsMask.
|
||||
// TODO(v8:7700): Change the condition below for Maglev OSR once it is
|
||||
// implemented.
|
||||
if (is_marked_for_any_optimization ||
|
||||
function.HasAvailableCodeKind(CodeKind::TURBOFAN)) {
|
||||
function.HasAvailableHigherTierCodeThanWithFilter(
|
||||
code_kind, kOptimizedJSFunctionCodeKindsMask)) {
|
||||
// OSR kicks in only once we've previously decided to tier up, but we are
|
||||
// still in the unoptimized frame (this implies a long-running loop).
|
||||
// still in the lower-tier frame (this implies a long-running loop).
|
||||
if (SmallEnoughForOSR(isolate_, function)) {
|
||||
TryIncrementOsrUrgency(isolate_, function);
|
||||
}
|
||||
@ -279,7 +282,8 @@ void TieringManager::MaybeOptimizeFrame(JSFunction function,
|
||||
}
|
||||
|
||||
DCHECK(!is_marked_for_any_optimization &&
|
||||
!function.HasAvailableCodeKind(CodeKind::TURBOFAN));
|
||||
!function.HasAvailableHigherTierCodeThanWithFilter(
|
||||
code_kind, kOptimizedJSFunctionCodeKindsMask));
|
||||
OptimizationDecision d = ShouldOptimize(function, code_kind);
|
||||
if (d.should_optimize()) Optimize(function, d);
|
||||
}
|
||||
|
@ -8,4 +8,10 @@ specific_include_rules = {
|
||||
"maglev-graph-builder\.h": [
|
||||
"+src/interpreter/interpreter-intrinsics.h",
|
||||
],
|
||||
}
|
||||
"maglev-ir\.cc": [
|
||||
# Allow Maglev to reuse the baseline assembler.
|
||||
# TODO(v8:7700): Clean up these dependencies by extracting common code to a
|
||||
# separate directory.
|
||||
"+src/baseline/baseline-assembler-inl.h",
|
||||
],
|
||||
}
|
||||
|
@ -2361,13 +2361,14 @@ void MaglevGraphBuilder::VisitJumpLoop() {
|
||||
if (relative_jump_bytecode_offset > 0) {
|
||||
AddNewNode<ReduceInterruptBudget>({}, relative_jump_bytecode_offset);
|
||||
}
|
||||
AddNewNode<JumpLoopPrologue>({}, loop_offset, feedback_slot,
|
||||
BytecodeOffset(iterator_.current_offset()),
|
||||
compilation_unit_);
|
||||
BasicBlock* block =
|
||||
target == iterator_.current_offset()
|
||||
? FinishBlock<JumpLoop>(next_offset(), {}, &jump_targets_[target],
|
||||
loop_offset, feedback_slot)
|
||||
? FinishBlock<JumpLoop>(next_offset(), {}, &jump_targets_[target])
|
||||
: FinishBlock<JumpLoop>(next_offset(), {},
|
||||
jump_targets_[target].block_ptr(),
|
||||
loop_offset, feedback_slot);
|
||||
jump_targets_[target].block_ptr());
|
||||
|
||||
merge_states_[target]->MergeLoop(*compilation_unit_,
|
||||
current_interpreter_frame_, block, target);
|
||||
|
@ -94,6 +94,7 @@ class MaglevGraphVerifier {
|
||||
case Opcode::kJump:
|
||||
case Opcode::kJumpFromInlined:
|
||||
case Opcode::kJumpLoop:
|
||||
case Opcode::kJumpLoopPrologue:
|
||||
case Opcode::kJumpToInlined:
|
||||
case Opcode::kRegisterInput:
|
||||
case Opcode::kRootConstant:
|
||||
|
45
src/maglev/maglev-ir-inl.h
Normal file
45
src/maglev/maglev-ir-inl.h
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_MAGLEV_MAGLEV_IR_INL_H_
|
||||
#define V8_MAGLEV_MAGLEV_IR_INL_H_
|
||||
|
||||
#include "src/maglev/maglev-interpreter-frame-state.h"
|
||||
#include "src/maglev/maglev-ir.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace maglev {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Function>
|
||||
void DeepForEachInputImpl(const MaglevCompilationUnit& unit,
|
||||
const CheckpointedInterpreterState* state,
|
||||
InputLocation* input_locations, int& index,
|
||||
Function&& f) {
|
||||
if (state->parent) {
|
||||
DeepForEachInputImpl(*unit.caller(), state->parent, input_locations, index,
|
||||
f);
|
||||
}
|
||||
state->register_frame->ForEachValue(
|
||||
unit, [&](ValueNode* node, interpreter::Register reg) {
|
||||
f(node, reg, &input_locations[index++]);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void DeepForEachInput(const EagerDeoptInfo* node, Function&& f) {
|
||||
int index = 0;
|
||||
DeepForEachInputImpl(node->unit, &node->state, node->input_locations, index,
|
||||
f);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace maglev
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_MAGLEV_MAGLEV_IR_INL_H_
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "src/base/bits.h"
|
||||
#include "src/base/logging.h"
|
||||
#include "src/baseline/baseline-assembler-inl.h"
|
||||
#include "src/builtins/builtins-constructor.h"
|
||||
#include "src/codegen/interface-descriptors-inl.h"
|
||||
#include "src/codegen/interface-descriptors.h"
|
||||
@ -25,6 +26,7 @@
|
||||
#include "src/maglev/maglev-graph-printer.h"
|
||||
#include "src/maglev/maglev-graph-processor.h"
|
||||
#include "src/maglev/maglev-interpreter-frame-state.h"
|
||||
#include "src/maglev/maglev-ir-inl.h"
|
||||
#include "src/maglev/maglev-vreg-allocator.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -271,6 +273,10 @@ struct CopyForDeferredHelper<ZoneLabelRef>
|
||||
template <>
|
||||
struct CopyForDeferredHelper<RegisterSnapshot>
|
||||
: public CopyForDeferredByValue<RegisterSnapshot> {};
|
||||
// Feedback slots are copied by value.
|
||||
template <>
|
||||
struct CopyForDeferredHelper<FeedbackSlot>
|
||||
: public CopyForDeferredByValue<FeedbackSlot> {};
|
||||
|
||||
template <typename T>
|
||||
T CopyForDeferred(MaglevCompilationInfo* compilation_info, T&& value) {
|
||||
@ -288,13 +294,19 @@ T CopyForDeferred(MaglevCompilationInfo* compilation_info, const T& value) {
|
||||
return CopyForDeferredHelper<T>::Copy(compilation_info, value);
|
||||
}
|
||||
|
||||
template <typename Function, typename FunctionPointer = Function>
|
||||
template <typename Function>
|
||||
struct FunctionArgumentsTupleHelper
|
||||
: FunctionArgumentsTupleHelper<Function,
|
||||
decltype(&FunctionPointer::operator())> {};
|
||||
: public FunctionArgumentsTupleHelper<decltype(&Function::operator())> {};
|
||||
|
||||
template <typename T, typename C, typename R, typename... A>
|
||||
struct FunctionArgumentsTupleHelper<T, R (C::*)(A...) const> {
|
||||
template <typename C, typename R, typename... A>
|
||||
struct FunctionArgumentsTupleHelper<R (C::*)(A...) const> {
|
||||
using FunctionPointer = R (*)(A...);
|
||||
using Tuple = std::tuple<A...>;
|
||||
static constexpr size_t kSize = sizeof...(A);
|
||||
};
|
||||
|
||||
template <typename R, typename... A>
|
||||
struct FunctionArgumentsTupleHelper<R (&)(A...)> {
|
||||
using FunctionPointer = R (*)(A...);
|
||||
using Tuple = std::tuple<A...>;
|
||||
static constexpr size_t kSize = sizeof...(A);
|
||||
@ -358,6 +370,9 @@ DeferredCodeInfo* PushDeferredCode(MaglevCodeGenState* code_gen_state,
|
||||
return deferred_code;
|
||||
}
|
||||
|
||||
// Note this doesn't take capturing lambdas by design, since state may
|
||||
// change until `deferred_code_gen` is actually executed. Use either a
|
||||
// non-capturing lambda, or a plain function pointer.
|
||||
template <typename Function, typename... Args>
|
||||
void JumpToDeferredIf(Condition cond, MaglevCodeGenState* code_gen_state,
|
||||
Function&& deferred_code_gen, Args&&... args) {
|
||||
@ -3164,16 +3179,6 @@ void ThrowIfNotSuperConstructor::GenerateCode(
|
||||
this);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void AttemptOnStackReplacement(MaglevCodeGenState* code_gen_state,
|
||||
int32_t loop_depth, FeedbackSlot feedback_slot) {
|
||||
// TODO(v8:7700): Implement me. See also
|
||||
// InterpreterAssembler::OnStackReplacement.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ---
|
||||
// Control nodes
|
||||
// ---
|
||||
@ -3279,11 +3284,112 @@ void JumpFromInlined::GenerateCode(MaglevCodeGenState* code_gen_state,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void AttemptOnStackReplacement(MaglevCodeGenState* code_gen_state,
|
||||
Label* return_label, JumpLoopPrologue* node,
|
||||
Register scratch0, Register scratch1,
|
||||
int32_t loop_depth, FeedbackSlot feedback_slot,
|
||||
BytecodeOffset osr_offset) {
|
||||
// Two cases may cause us to attempt OSR, in the following order:
|
||||
//
|
||||
// 1) Presence of cached OSR Turbofan code.
|
||||
// 2) The OSR urgency exceeds the current loop depth - in that case, trigger
|
||||
// a Turbofan OSR compilation.
|
||||
//
|
||||
// See also: InterpreterAssembler::OnStackReplacement.
|
||||
|
||||
baseline::BaselineAssembler basm(code_gen_state->masm());
|
||||
|
||||
// Case 1).
|
||||
Label deopt;
|
||||
Register maybe_target_code = scratch1;
|
||||
{
|
||||
basm.TryLoadOptimizedOsrCode(maybe_target_code, scratch0, feedback_slot,
|
||||
&deopt, Label::kFar);
|
||||
}
|
||||
|
||||
// Case 2).
|
||||
{
|
||||
__ AssertFeedbackVector(scratch0);
|
||||
__ movb(scratch0, FieldOperand(scratch0, FeedbackVector::kOsrStateOffset));
|
||||
__ DecodeField<FeedbackVector::OsrUrgencyBits>(scratch0);
|
||||
basm.JumpIfByte(baseline::Condition::kUnsignedLessThanEqual, scratch0,
|
||||
loop_depth, return_label, Label::kNear);
|
||||
|
||||
// The osr_urgency exceeds the current loop_depth, signaling an OSR
|
||||
// request. Call into runtime to compile.
|
||||
{
|
||||
// At this point we need a custom register snapshot since additional
|
||||
// registers may be live at the eager deopt below (the normal
|
||||
// register_snapshot only contains live registers *after this
|
||||
// node*).
|
||||
// TODO(v8:7700): Consider making the snapshot location
|
||||
// configurable.
|
||||
RegisterSnapshot snapshot = node->register_snapshot();
|
||||
detail::DeepForEachInput(
|
||||
node->eager_deopt_info(),
|
||||
[&](ValueNode* node, interpreter::Register reg,
|
||||
InputLocation* input) {
|
||||
if (!input->IsAnyRegister()) return;
|
||||
if (input->IsDoubleRegister()) {
|
||||
snapshot.live_double_registers.set(
|
||||
input->AssignedDoubleRegister());
|
||||
} else {
|
||||
snapshot.live_registers.set(input->AssignedGeneralRegister());
|
||||
if (node->is_tagged()) {
|
||||
snapshot.live_tagged_registers.set(
|
||||
input->AssignedGeneralRegister());
|
||||
}
|
||||
}
|
||||
});
|
||||
SaveRegisterStateForCall save_register_state(code_gen_state, snapshot);
|
||||
__ Move(kContextRegister, code_gen_state->native_context().object());
|
||||
__ Push(Smi::FromInt(osr_offset.ToInt()));
|
||||
__ CallRuntime(Runtime::kCompileOptimizedOSRFromMaglev, 1);
|
||||
__ Move(scratch0, rax);
|
||||
}
|
||||
|
||||
// A `0` return value means there is no OSR code available yet. Fall
|
||||
// through for now, OSR code will be picked up once it exists and is
|
||||
// cached on the feedback vector.
|
||||
__ testq(scratch0, scratch0);
|
||||
__ j(equal, return_label, Label::kNear);
|
||||
}
|
||||
|
||||
__ bind(&deopt);
|
||||
EmitEagerDeopt(code_gen_state, node,
|
||||
DeoptimizeReason::kPrepareForOnStackReplacement);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void JumpLoopPrologue::AllocateVreg(MaglevVregAllocationState* vreg_state) {
|
||||
set_temporaries_needed(2);
|
||||
}
|
||||
void JumpLoopPrologue::GenerateCode(MaglevCodeGenState* code_gen_state,
|
||||
const ProcessingState& state) {
|
||||
Register scratch0 = temporaries().PopFirst();
|
||||
Register scratch1 = temporaries().PopFirst();
|
||||
|
||||
const Register osr_state = scratch1;
|
||||
__ Move(scratch0, unit_->feedback().object());
|
||||
__ AssertFeedbackVector(scratch0);
|
||||
__ movb(osr_state, FieldOperand(scratch0, FeedbackVector::kOsrStateOffset));
|
||||
|
||||
// The quick initial OSR check. If it passes, we proceed on to more
|
||||
// expensive OSR logic.
|
||||
static_assert(FeedbackVector::MaybeHasOptimizedOsrCodeBit::encode(true) >
|
||||
FeedbackVector::kMaxOsrUrgency);
|
||||
__ cmpl(osr_state, Immediate(loop_depth_));
|
||||
JumpToDeferredIf(above, code_gen_state, AttemptOnStackReplacement, this,
|
||||
scratch0, scratch1, loop_depth_, feedback_slot_,
|
||||
osr_offset_);
|
||||
}
|
||||
|
||||
void JumpLoop::AllocateVreg(MaglevVregAllocationState* vreg_state) {}
|
||||
void JumpLoop::GenerateCode(MaglevCodeGenState* code_gen_state,
|
||||
const ProcessingState& state) {
|
||||
AttemptOnStackReplacement(code_gen_state, loop_depth_, feedback_slot_);
|
||||
|
||||
__ jmp(target()->label());
|
||||
}
|
||||
|
||||
|
@ -191,6 +191,7 @@ class CompactInterpreterFrameState;
|
||||
V(CheckString) \
|
||||
V(CheckMapsWithMigration) \
|
||||
V(GeneratorStore) \
|
||||
V(JumpLoopPrologue) \
|
||||
V(StoreTaggedFieldNoWriteBarrier) \
|
||||
V(StoreTaggedFieldWithWriteBarrier) \
|
||||
V(IncreaseInterruptBudget) \
|
||||
@ -573,6 +574,7 @@ class ValueLocation {
|
||||
return compiler::AllocatedOperand::cast(operand_).GetDoubleRegister();
|
||||
}
|
||||
|
||||
bool IsAnyRegister() const { return operand_.IsAnyRegister(); }
|
||||
bool IsDoubleRegister() const { return operand_.IsDoubleRegister(); }
|
||||
|
||||
const compiler::InstructionOperand& operand() const { return operand_; }
|
||||
@ -832,7 +834,7 @@ class NodeBase : public ZoneObject {
|
||||
return NumTemporariesNeededField::decode(bitfield_);
|
||||
}
|
||||
|
||||
RegList temporaries() const { return temporaries_; }
|
||||
RegList& temporaries() { return temporaries_; }
|
||||
|
||||
void assign_temporaries(RegList list) { temporaries_ = list; }
|
||||
|
||||
@ -1109,6 +1111,11 @@ class ValueNode : public Node {
|
||||
ValueRepresentation::kFloat64);
|
||||
}
|
||||
|
||||
constexpr bool is_tagged() const {
|
||||
return (properties().value_representation() ==
|
||||
ValueRepresentation::kTagged);
|
||||
}
|
||||
|
||||
constexpr MachineRepresentation GetMachineRepresentation() const {
|
||||
switch (properties().value_representation()) {
|
||||
case ValueRepresentation::kTagged:
|
||||
@ -1959,6 +1966,35 @@ class GeneratorStore : public NodeT<GeneratorStore> {
|
||||
const int bytecode_offset_;
|
||||
};
|
||||
|
||||
class JumpLoopPrologue : public FixedInputNodeT<0, JumpLoopPrologue> {
|
||||
using Base = FixedInputNodeT<0, JumpLoopPrologue>;
|
||||
|
||||
public:
|
||||
explicit JumpLoopPrologue(uint64_t bitfield, int32_t loop_depth,
|
||||
FeedbackSlot feedback_slot,
|
||||
BytecodeOffset osr_offset,
|
||||
MaglevCompilationUnit* unit)
|
||||
: Base(bitfield),
|
||||
loop_depth_(loop_depth),
|
||||
feedback_slot_(feedback_slot),
|
||||
osr_offset_(osr_offset),
|
||||
unit_(unit) {}
|
||||
|
||||
static constexpr OpProperties kProperties =
|
||||
OpProperties::NeedsRegisterSnapshot() | OpProperties::EagerDeopt();
|
||||
|
||||
void AllocateVreg(MaglevVregAllocationState*);
|
||||
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
|
||||
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
|
||||
|
||||
private:
|
||||
// For OSR.
|
||||
const int32_t loop_depth_;
|
||||
const FeedbackSlot feedback_slot_;
|
||||
const BytecodeOffset osr_offset_;
|
||||
MaglevCompilationUnit* const unit_;
|
||||
};
|
||||
|
||||
class ForInPrepare : public FixedInputValueNodeT<2, ForInPrepare> {
|
||||
using Base = FixedInputValueNodeT<2, ForInPrepare>;
|
||||
|
||||
@ -3657,7 +3693,7 @@ class UnconditionalControlNodeT : public UnconditionalControlNode {
|
||||
|
||||
class ConditionalControlNode : public ControlNode {
|
||||
public:
|
||||
ConditionalControlNode(uint64_t bitfield) : ControlNode(bitfield) {}
|
||||
explicit ConditionalControlNode(uint64_t bitfield) : ControlNode(bitfield) {}
|
||||
};
|
||||
|
||||
class BranchControlNode : public ConditionalControlNode {
|
||||
@ -3715,17 +3751,11 @@ class JumpLoop : public UnconditionalControlNodeT<JumpLoop> {
|
||||
using Base = UnconditionalControlNodeT<JumpLoop>;
|
||||
|
||||
public:
|
||||
explicit JumpLoop(uint64_t bitfield, BasicBlock* target, int32_t loop_depth,
|
||||
FeedbackSlot feedback_slot)
|
||||
: Base(bitfield, target),
|
||||
loop_depth_(loop_depth),
|
||||
feedback_slot_(feedback_slot) {}
|
||||
explicit JumpLoop(uint64_t bitfield, BasicBlock* target)
|
||||
: Base(bitfield, target) {}
|
||||
|
||||
explicit JumpLoop(uint64_t bitfield, BasicBlockRef* ref, int32_t loop_depth,
|
||||
FeedbackSlot feedback_slot)
|
||||
: Base(bitfield, ref),
|
||||
loop_depth_(loop_depth),
|
||||
feedback_slot_(feedback_slot) {}
|
||||
explicit JumpLoop(uint64_t bitfield, BasicBlockRef* ref)
|
||||
: Base(bitfield, ref) {}
|
||||
|
||||
void AllocateVreg(MaglevVregAllocationState*);
|
||||
void GenerateCode(MaglevCodeGenState*, const ProcessingState&);
|
||||
@ -3737,9 +3767,6 @@ class JumpLoop : public UnconditionalControlNodeT<JumpLoop> {
|
||||
}
|
||||
|
||||
private:
|
||||
// For OSR.
|
||||
const int32_t loop_depth_;
|
||||
const FeedbackSlot feedback_slot_;
|
||||
base::Vector<Input> used_node_locations_;
|
||||
};
|
||||
|
||||
@ -3758,7 +3785,7 @@ class JumpToInlined : public UnconditionalControlNodeT<JumpToInlined> {
|
||||
const MaglevCompilationUnit* unit() const { return unit_; }
|
||||
|
||||
private:
|
||||
MaglevCompilationUnit* unit_;
|
||||
MaglevCompilationUnit* const unit_;
|
||||
};
|
||||
|
||||
class JumpFromInlined : public UnconditionalControlNodeT<JumpFromInlined> {
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "src/maglev/maglev-graph-processor.h"
|
||||
#include "src/maglev/maglev-graph.h"
|
||||
#include "src/maglev/maglev-interpreter-frame-state.h"
|
||||
#include "src/maglev/maglev-ir.h"
|
||||
#include "src/maglev/maglev-ir-inl.h"
|
||||
#include "src/maglev/maglev-regalloc-data.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -472,25 +472,13 @@ void StraightForwardRegisterAllocator::UpdateUse(
|
||||
|
||||
void StraightForwardRegisterAllocator::UpdateUse(
|
||||
const EagerDeoptInfo& deopt_info) {
|
||||
int index = 0;
|
||||
UpdateUse(deopt_info.unit, &deopt_info.state, deopt_info.input_locations,
|
||||
index);
|
||||
}
|
||||
|
||||
void StraightForwardRegisterAllocator::UpdateUse(
|
||||
const LazyDeoptInfo& deopt_info) {
|
||||
const CompactInterpreterFrameState* checkpoint_state =
|
||||
deopt_info.state.register_frame;
|
||||
int index = 0;
|
||||
checkpoint_state->ForEachValue(
|
||||
deopt_info.unit, [&](ValueNode* node, interpreter::Register reg) {
|
||||
// Skip over the result location.
|
||||
if (reg == deopt_info.result_location) return;
|
||||
detail::DeepForEachInput(
|
||||
&deopt_info,
|
||||
[&](ValueNode* node, interpreter::Register reg, InputLocation* input) {
|
||||
if (FLAG_trace_maglev_regalloc) {
|
||||
printing_visitor_->os()
|
||||
<< "- using " << PrintNodeLabel(graph_labeller(), node) << "\n";
|
||||
}
|
||||
InputLocation* input = &deopt_info.input_locations[index++];
|
||||
// We might have dropped this node without spilling it. Spill it now.
|
||||
if (!node->has_register() && !node->is_loadable()) {
|
||||
Spill(node);
|
||||
@ -501,20 +489,21 @@ 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;
|
||||
const LazyDeoptInfo& deopt_info) {
|
||||
const CompactInterpreterFrameState* checkpoint_state =
|
||||
deopt_info.state.register_frame;
|
||||
int index = 0;
|
||||
// TODO(leszeks): This is missing parent recursion, fix it.
|
||||
// See also: UpdateUse(EagerDeoptInfo&).
|
||||
checkpoint_state->ForEachValue(
|
||||
unit, [&](ValueNode* node, interpreter::Register reg) {
|
||||
deopt_info.unit, [&](ValueNode* node, interpreter::Register reg) {
|
||||
// Skip over the result location.
|
||||
if (reg == deopt_info.result_location) return;
|
||||
if (FLAG_trace_maglev_regalloc) {
|
||||
printing_visitor_->os()
|
||||
<< "- using " << PrintNodeLabel(graph_labeller(), node) << "\n";
|
||||
}
|
||||
InputLocation* input = &input_locations[index++];
|
||||
InputLocation* input = &deopt_info.input_locations[index++];
|
||||
// We might have dropped this node without spilling it. Spill it now.
|
||||
if (!node->has_register() && !node->is_loadable()) {
|
||||
Spill(node);
|
||||
|
@ -134,9 +134,6 @@ class StraightForwardRegisterAllocator {
|
||||
void UpdateUse(ValueNode* node, InputLocation* input_location);
|
||||
void UpdateUse(const EagerDeoptInfo& 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 AllocateNode(Node* node);
|
||||
|
@ -1809,7 +1809,7 @@ DEFINE_DEOPT_ENTRY_ACCESSORS(Pc, Smi)
|
||||
DEFINE_DEOPT_ENTRY_ACCESSORS(NodeId, Smi)
|
||||
#endif // DEBUG
|
||||
|
||||
BytecodeOffset DeoptimizationData::GetBytecodeOffset(int i) {
|
||||
BytecodeOffset DeoptimizationData::GetBytecodeOffset(int i) const {
|
||||
return BytecodeOffset(BytecodeOffsetRaw(i).value());
|
||||
}
|
||||
|
||||
|
@ -1385,7 +1385,7 @@ class DeoptimizationData : public FixedArray {
|
||||
|
||||
#undef DECL_ENTRY_ACCESSORS
|
||||
|
||||
inline BytecodeOffset GetBytecodeOffset(int i);
|
||||
inline BytecodeOffset GetBytecodeOffset(int i) const;
|
||||
|
||||
inline void SetBytecodeOffset(int i, BytecodeOffset value);
|
||||
|
||||
|
@ -66,11 +66,19 @@ bool JSFunction::HasAttachedOptimizedCode() const {
|
||||
}
|
||||
|
||||
bool JSFunction::HasAvailableHigherTierCodeThan(CodeKind kind) const {
|
||||
return HasAvailableHigherTierCodeThanWithFilter(kind,
|
||||
kJSFunctionCodeKindsMask);
|
||||
}
|
||||
|
||||
bool JSFunction::HasAvailableHigherTierCodeThanWithFilter(
|
||||
CodeKind kind, CodeKinds filter_mask) const {
|
||||
const int kind_as_int_flag = static_cast<int>(CodeKindToCodeKindFlag(kind));
|
||||
DCHECK(base::bits::IsPowerOfTwo(kind_as_int_flag));
|
||||
// Smear right - any higher present bit means we have a higher tier available.
|
||||
const int mask = kind_as_int_flag | (kind_as_int_flag - 1);
|
||||
return (GetAvailableCodeKinds() & static_cast<CodeKinds>(~mask)) != 0;
|
||||
const CodeKinds masked_available_kinds =
|
||||
GetAvailableCodeKinds() & filter_mask;
|
||||
return (masked_available_kinds & static_cast<CodeKinds>(~mask)) != 0;
|
||||
}
|
||||
|
||||
bool JSFunction::HasAvailableOptimizedCode() const {
|
||||
|
@ -153,6 +153,9 @@ class JSFunction : public TorqueGeneratedJSFunction<
|
||||
// will happen on its next activation.
|
||||
|
||||
bool HasAvailableHigherTierCodeThan(CodeKind kind) const;
|
||||
// As above but only considers available code kinds passing the filter mask.
|
||||
bool HasAvailableHigherTierCodeThanWithFilter(CodeKind kind,
|
||||
CodeKinds filter_mask) const;
|
||||
|
||||
// True, iff any generated code kind is attached/available to this function.
|
||||
V8_EXPORT_PRIVATE bool HasAttachedOptimizedCode() const;
|
||||
|
@ -324,6 +324,8 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
|
||||
// code object from deoptimizer.
|
||||
Handle<Code> optimized_code = deoptimizer->compiled_code();
|
||||
const DeoptimizeKind deopt_kind = deoptimizer->deopt_kind();
|
||||
const DeoptimizeReason deopt_reason =
|
||||
deoptimizer->GetDeoptInfo().deopt_reason;
|
||||
|
||||
// TODO(turbofan): We currently need the native context to materialize
|
||||
// the arguments object, but only to get to its map.
|
||||
@ -347,6 +349,12 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
// Some eager deopts also don't invalidate Code (e.g. when preparing for OSR
|
||||
// from Maglev to Turbofan).
|
||||
if (IsDeoptimizationWithoutCodeInvalidation(deopt_reason)) {
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
// Non-OSR'd code is deoptimized unconditionally. If the deoptimization occurs
|
||||
// inside the outermost loop containning a loop that can trigger OSR
|
||||
// compilation, we remove the OSR code, it will avoid hit the out of date OSR
|
||||
@ -387,25 +395,30 @@ RUNTIME_FUNCTION(Runtime_VerifyType) {
|
||||
return *obj;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileOptimizedOSR) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
DCHECK(FLAG_use_osr);
|
||||
namespace {
|
||||
|
||||
void GetOsrOffsetAndFunctionForOSR(Isolate* isolate, BytecodeOffset* osr_offset,
|
||||
Handle<JSFunction>* function) {
|
||||
DCHECK(osr_offset->IsNone());
|
||||
DCHECK(function->is_null());
|
||||
|
||||
// Determine the frame that triggered the OSR request.
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
UnoptimizedFrame* frame = UnoptimizedFrame::cast(it.frame());
|
||||
|
||||
DCHECK_IMPLIES(frame->is_interpreted(),
|
||||
frame->LookupCodeT().is_interpreter_trampoline_builtin());
|
||||
DCHECK_IMPLIES(frame->is_baseline(),
|
||||
frame->LookupCodeT().kind() == CodeKind::BASELINE);
|
||||
DCHECK(frame->function().shared().HasBytecodeArray());
|
||||
|
||||
// Determine the entry point for which this OSR request has been fired.
|
||||
BytecodeOffset osr_offset = BytecodeOffset(frame->GetBytecodeOffset());
|
||||
DCHECK(!osr_offset.IsNone());
|
||||
*osr_offset = BytecodeOffset(frame->GetBytecodeOffset());
|
||||
*function = handle(frame->function(), isolate);
|
||||
|
||||
DCHECK(!osr_offset->IsNone());
|
||||
DCHECK((*function)->shared().HasBytecodeArray());
|
||||
}
|
||||
|
||||
Object CompileOptimizedOSR(Isolate* isolate, Handle<JSFunction> function,
|
||||
BytecodeOffset osr_offset) {
|
||||
const ConcurrencyMode mode =
|
||||
V8_LIKELY(isolate->concurrent_recompilation_enabled() &&
|
||||
FLAG_concurrent_osr)
|
||||
@ -413,7 +426,6 @@ RUNTIME_FUNCTION(Runtime_CompileOptimizedOSR) {
|
||||
: ConcurrencyMode::kSynchronous;
|
||||
|
||||
Handle<CodeT> result;
|
||||
Handle<JSFunction> function(frame->function(), isolate);
|
||||
if (!Compiler::CompileOptimizedOSR(isolate, function, osr_offset, mode)
|
||||
.ToHandle(&result)) {
|
||||
// An empty result can mean one of two things:
|
||||
@ -456,20 +468,44 @@ RUNTIME_FUNCTION(Runtime_CompileOptimizedOSR) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileOptimizedOSR) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
DCHECK(FLAG_use_osr);
|
||||
|
||||
BytecodeOffset osr_offset = BytecodeOffset::None();
|
||||
Handle<JSFunction> function;
|
||||
GetOsrOffsetAndFunctionForOSR(isolate, &osr_offset, &function);
|
||||
|
||||
return CompileOptimizedOSR(isolate, function, osr_offset);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompileOptimizedOSRFromMaglev) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
DCHECK(FLAG_use_osr);
|
||||
|
||||
const BytecodeOffset osr_offset(args.positive_smi_value_at(0));
|
||||
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
MaglevFrame* frame = MaglevFrame::cast(it.frame());
|
||||
DCHECK_EQ(frame->LookupCodeT().kind(), CodeKind::MAGLEV);
|
||||
Handle<JSFunction> function = handle(frame->function(), isolate);
|
||||
|
||||
return CompileOptimizedOSR(isolate, function, osr_offset);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TraceOptimizedOSREntry) {
|
||||
HandleScope handle_scope(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
CHECK(FLAG_trace_osr);
|
||||
|
||||
// Determine the frame that triggered the OSR request.
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
UnoptimizedFrame* frame = UnoptimizedFrame::cast(it.frame());
|
||||
BytecodeOffset osr_offset = BytecodeOffset::None();
|
||||
Handle<JSFunction> function;
|
||||
GetOsrOffsetAndFunctionForOSR(isolate, &osr_offset, &function);
|
||||
|
||||
// Determine the entry point for which this OSR request has been fired.
|
||||
BytecodeOffset osr_offset = BytecodeOffset(frame->GetBytecodeOffset());
|
||||
DCHECK(!osr_offset.IsNone());
|
||||
|
||||
Handle<JSFunction> function(frame->function(), isolate);
|
||||
PrintF(CodeTracer::Scope{isolate->GetCodeTracer()}.file(),
|
||||
"[OSR - entry. function: %s, osr offset: %d]\n",
|
||||
function->DebugNameCStr().get(), osr_offset.ToInt());
|
||||
|
@ -492,6 +492,13 @@ RUNTIME_FUNCTION(Runtime_IsTurbofanEnabled) {
|
||||
return isolate->heap()->ToBoolean(FLAG_turbofan);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CurrentFrameIsTurbofan) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(args.length(), 0);
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
return isolate->heap()->ToBoolean(it.frame()->is_turbofan());
|
||||
}
|
||||
|
||||
#ifdef V8_ENABLE_MAGLEV
|
||||
RUNTIME_FUNCTION(Runtime_OptimizeMaglevOnNextCall) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -106,6 +106,7 @@ namespace internal {
|
||||
|
||||
#define FOR_EACH_INTRINSIC_COMPILER(F, I) \
|
||||
F(CompileOptimizedOSR, 0, 1) \
|
||||
F(CompileOptimizedOSRFromMaglev, 1, 1) \
|
||||
F(TraceOptimizedOSREntry, 0, 1) \
|
||||
F(CompileLazy, 1, 1) \
|
||||
F(CompileBaseline, 1, 1) \
|
||||
@ -491,6 +492,7 @@ namespace internal {
|
||||
F(ConstructInternalizedString, 1, 1) \
|
||||
F(ConstructSlicedString, 2, 1) \
|
||||
F(ConstructThinString, 1, 1) \
|
||||
F(CurrentFrameIsTurbofan, 0, 1) \
|
||||
F(DebugPrint, 1, 1) \
|
||||
F(DebugPrintPtr, 1, 1) \
|
||||
F(DebugTrace, 0, 1) \
|
||||
|
35
test/mjsunit/maglev/osr-from-ml-to-tf.js
Normal file
35
test/mjsunit/maglev/osr-from-ml-to-tf.js
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 --no-stress-opt
|
||||
// Flags: --no-baseline-batch-compilation --use-osr --turbofan
|
||||
|
||||
let keep_going = 10000000; // A counter to avoid test hangs on failure.
|
||||
let i; // The loop counter for the test function.
|
||||
|
||||
function f() {
|
||||
let sum = i;
|
||||
while (--i > 0 && !%CurrentFrameIsTurbofan() && --keep_going) {
|
||||
// This loop should trigger OSR.
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
function g() {
|
||||
assertTrue(%IsMaglevEnabled());
|
||||
while (!%ActiveTierIsMaglev(f) && --keep_going) {
|
||||
i = 5.2;
|
||||
f();
|
||||
}
|
||||
|
||||
console.log('osr to tf', keep_going);
|
||||
assertTrue(%IsTurbofanEnabled());
|
||||
i = 66666666666;
|
||||
f();
|
||||
assertTrue(keep_going > 0);
|
||||
}
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
g();
|
25
test/mjsunit/maglev/osr-to-tf.js
Normal file
25
test/mjsunit/maglev/osr-to-tf.js
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 --no-stress-opt
|
||||
// Flags: --no-baseline-batch-compilation --use-osr --turbofan
|
||||
|
||||
let keep_going = 100000; // A counter to avoid test hangs on failure.
|
||||
|
||||
function f() {
|
||||
let reached_tf = false;
|
||||
while (!reached_tf && --keep_going) {
|
||||
// This loop should trigger OSR.
|
||||
reached_tf = %CurrentFrameIsTurbofan();
|
||||
}
|
||||
}
|
||||
|
||||
function g() {
|
||||
assertTrue(%IsTurbofanEnabled());
|
||||
f();
|
||||
assertTrue(keep_going > 0);
|
||||
}
|
||||
%NeverOptimizeFunction(g);
|
||||
|
||||
g();
|
Loading…
Reference in New Issue
Block a user