Commit 608018e5 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[nci] Implement tier-up (part 3, spawn task & install)

This is the final part of the tier-up commit series. It implements:

- A prologue in NCI code objects that checks and acts upon the
optimization marker.
- Currently, handling is deferred to the InterpreterEntryTrampoline
but this will change in the future.
- The lifecycle is otherwise like Ignition-to-Turbofan; the runtime
profiler marks a function for optimization, the next call to that
function triggers optimization by calling into runtime, and the
finished code object is installed both on the JSFunction and the
optimized code cache.
- The feedback vector's kOptimizedCodeWeakOrSmiOffset slot is
currently reused for the mid-to-top tier up.

Cq-Include-Trybots: luci.v8.try:v8_linux64_fyi_rel_ng
Bug: v8:8888
Change-Id: Iff50b05ddcc68b25d7ed0f1e0d20af076a1522a0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2361466Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69808}
parent 27f34962
...@@ -66,6 +66,27 @@ bool IsForNativeContextIndependentCachingOnly(CodeKind kind) { ...@@ -66,6 +66,27 @@ bool IsForNativeContextIndependentCachingOnly(CodeKind kind) {
!FLAG_turbo_nci_as_midtier; !FLAG_turbo_nci_as_midtier;
} }
// This predicate is currently needed only because the nci-as-midtier testing
// configuration is special. A quick summary of compilation configurations:
//
// - Turbofan (and currently Turboprop) uses both the optimization marker and
// the optimized code cache (underneath, the marker and the cache share the same
// slot on the feedback vector).
// - Native context independent (NCI) code uses neither the marker nor the
// cache.
// - The NCI-as-midtier testing configuration uses the marker, but not the
// cache.
//
// This predicate supports that last case. In the near future, this last case is
// expected to change s.t. code kinds use the marker iff they use the optimized
// code cache (details still TBD). In that case, the existing
// CodeKindIsStoredInOptimizedCodeCache is sufficient and this extra predicate
// can be removed.
// TODO(jgruber,rmcilroy,v8:8888): Remove this predicate once that has happened.
bool UsesOptimizationMarker(CodeKind kind) {
return !IsForNativeContextIndependentCachingOnly(kind);
}
class CompilerTracer : public AllStatic { class CompilerTracer : public AllStatic {
public: public:
static void PrintTracePrefix(const CodeTracer::Scope& scope, static void PrintTracePrefix(const CodeTracer::Scope& scope,
...@@ -843,7 +864,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache( ...@@ -843,7 +864,7 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache(
} }
void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) { void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) {
DCHECK(CodeKindIsStoredInOptimizedCodeCache(compilation_info->code_kind())); DCHECK(UsesOptimizationMarker(compilation_info->code_kind()));
Handle<JSFunction> function = compilation_info->closure(); Handle<JSFunction> function = compilation_info->closure();
if (compilation_info->osr_offset().IsNone()) { if (compilation_info->osr_offset().IsNone()) {
Handle<FeedbackVector> vector = Handle<FeedbackVector> vector =
...@@ -855,7 +876,12 @@ void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) { ...@@ -855,7 +876,12 @@ void ClearOptimizedCodeCache(OptimizedCompilationInfo* compilation_info) {
void InsertCodeIntoOptimizedCodeCache( void InsertCodeIntoOptimizedCodeCache(
OptimizedCompilationInfo* compilation_info) { OptimizedCompilationInfo* compilation_info) {
const CodeKind kind = compilation_info->code_kind(); const CodeKind kind = compilation_info->code_kind();
if (!CodeKindIsStoredInOptimizedCodeCache(kind)) return; if (!CodeKindIsStoredInOptimizedCodeCache(kind)) {
if (UsesOptimizationMarker(kind)) {
ClearOptimizedCodeCache(compilation_info);
}
return;
}
if (compilation_info->function_context_specializing()) { if (compilation_info->function_context_specializing()) {
// Function context specialization folds-in the function context, so no // Function context specialization folds-in the function context, so no
...@@ -987,11 +1013,26 @@ bool GetOptimizedCodeLater(std::unique_ptr<OptimizedCompilationJob> job, ...@@ -987,11 +1013,26 @@ bool GetOptimizedCodeLater(std::unique_ptr<OptimizedCompilationJob> job,
if (CodeKindIsStoredInOptimizedCodeCache(code_kind)) { if (CodeKindIsStoredInOptimizedCodeCache(code_kind)) {
function->SetOptimizationMarker(OptimizationMarker::kInOptimizationQueue); function->SetOptimizationMarker(OptimizationMarker::kInOptimizationQueue);
} }
DCHECK(function->ActiveTierIsIgnition()); DCHECK(function->ActiveTierIsIgnition() || function->ActiveTierIsNCI());
DCHECK(function->shared().HasBytecodeArray()); DCHECK(function->shared().HasBytecodeArray());
return true; return true;
} }
// Returns the code object at which execution continues after a concurrent
// optimization job has been started (but not finished).
Handle<Code> ContinuationForConcurrentOptimization(
Isolate* isolate, Handle<JSFunction> function) {
Handle<Code> cached_code;
if (FLAG_turbo_nci && function->NextTier() == CodeKindForTopTier() &&
GetCodeFromCompilationCache(isolate, handle(function->shared(), isolate))
.ToHandle(&cached_code)) {
// Tiering up to Turbofan and cached optimized code exists. Continue
// execution there until TF optimization has finished.
return cached_code;
}
return BUILTIN_CODE(isolate, InterpreterEntryTrampoline);
}
MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function, MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
ConcurrencyMode mode, CodeKind code_kind, ConcurrencyMode mode, CodeKind code_kind,
BailoutId osr_offset = BailoutId::None(), BailoutId osr_offset = BailoutId::None(),
...@@ -1006,8 +1047,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function, ...@@ -1006,8 +1047,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
// If compiling for NCI caching only (which does not use the optimization // If compiling for NCI caching only (which does not use the optimization
// marker), don't touch the marker to avoid interfering with Turbofan // marker), don't touch the marker to avoid interfering with Turbofan
// compilation. // compilation.
if (CodeKindIsStoredInOptimizedCodeCache(code_kind) && if (UsesOptimizationMarker(code_kind) && function->HasOptimizationMarker()) {
function->HasOptimizationMarker()) {
function->ClearOptimizationMarker(); function->ClearOptimizationMarker();
} }
...@@ -1077,7 +1117,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function, ...@@ -1077,7 +1117,7 @@ MaybeHandle<Code> GetOptimizedCode(Handle<JSFunction> function,
if (mode == ConcurrencyMode::kConcurrent) { if (mode == ConcurrencyMode::kConcurrent) {
if (GetOptimizedCodeLater(std::move(job), isolate, compilation_info, if (GetOptimizedCodeLater(std::move(job), isolate, compilation_info,
code_kind, function)) { code_kind, function)) {
return BUILTIN_CODE(isolate, InterpreterEntryTrampoline); return ContinuationForConcurrentOptimization(isolate, function);
} }
} else { } else {
DCHECK_EQ(mode, ConcurrencyMode::kNotConcurrent); DCHECK_EQ(mode, ConcurrencyMode::kNotConcurrent);
...@@ -1837,7 +1877,8 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function, ...@@ -1837,7 +1877,8 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function,
ConcurrencyMode mode, CodeKind code_kind) { ConcurrencyMode mode, CodeKind code_kind) {
DCHECK(CodeKindIsOptimizedJSFunction(code_kind)); DCHECK(CodeKindIsOptimizedJSFunction(code_kind));
if (function->HasAttachedOptimizedCode()) return true; // If the requested code kind is already available, do nothing.
if (function->HasAvailableCodeKind(code_kind)) return true;
Isolate* isolate = function->GetIsolate(); Isolate* isolate = function->GetIsolate();
DCHECK(AllowCompilation::IsAllowed(isolate)); DCHECK(AllowCompilation::IsAllowed(isolate));
...@@ -1860,7 +1901,7 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function, ...@@ -1860,7 +1901,7 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function,
DCHECK(!isolate->has_pending_exception()); DCHECK(!isolate->has_pending_exception());
DCHECK(function->shared().is_compiled()); DCHECK(function->shared().is_compiled());
DCHECK(function->is_compiled()); DCHECK(function->is_compiled());
if (CodeKindIsStoredInOptimizedCodeCache(code_kind)) { if (UsesOptimizationMarker(code_kind)) {
DCHECK_IMPLIES(function->HasOptimizationMarker(), DCHECK_IMPLIES(function->HasOptimizationMarker(),
function->IsInOptimizationQueue()); function->IsInOptimizationQueue());
DCHECK_IMPLIES(function->HasOptimizationMarker(), DCHECK_IMPLIES(function->HasOptimizationMarker(),
...@@ -2974,7 +3015,7 @@ bool Compiler::FinalizeOptimizedCompilationJob(OptimizedCompilationJob* job, ...@@ -2974,7 +3015,7 @@ bool Compiler::FinalizeOptimizedCompilationJob(OptimizedCompilationJob* job,
CompilerTracer::TraceAbortedJob(isolate, compilation_info); CompilerTracer::TraceAbortedJob(isolate, compilation_info);
compilation_info->closure()->set_code(shared->GetCode()); compilation_info->closure()->set_code(shared->GetCode());
// Clear the InOptimizationQueue marker, if it exists. // Clear the InOptimizationQueue marker, if it exists.
if (CodeKindIsStoredInOptimizedCodeCache(code_kind) && if (UsesOptimizationMarker(code_kind) &&
compilation_info->closure()->IsInOptimizationQueue()) { compilation_info->closure()->IsInOptimizationQueue()) {
compilation_info->closure()->ClearOptimizationMarker(); compilation_info->closure()->ClearOptimizationMarker();
} }
......
...@@ -214,7 +214,7 @@ void OptimizingCompileDispatcher::InstallOptimizedFunctions() { ...@@ -214,7 +214,7 @@ void OptimizingCompileDispatcher::InstallOptimizedFunctions() {
} }
OptimizedCompilationInfo* info = job->compilation_info(); OptimizedCompilationInfo* info = job->compilation_info();
Handle<JSFunction> function(*info->closure(), isolate_); Handle<JSFunction> function(*info->closure(), isolate_);
if (function->HasAvailableOptimizedCode()) { if (function->HasAvailableCodeKind(info->code_kind())) {
if (FLAG_trace_concurrent_recompilation) { if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Aborting compilation for "); PrintF(" ** Aborting compilation for ");
function->ShortPrint(); function->ShortPrint();
......
...@@ -1248,6 +1248,16 @@ FieldAccess AccessBuilder::ForFeedbackVectorClosureFeedbackCellArray() { ...@@ -1248,6 +1248,16 @@ FieldAccess AccessBuilder::ForFeedbackVectorClosureFeedbackCellArray() {
return access; return access;
} }
// static
FieldAccess AccessBuilder::ForFeedbackVectorOptimizedCodeWeakOrSmi() {
FieldAccess access = {
kTaggedBase, FeedbackVector::kOptimizedCodeWeakOrSmiOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::Any(), MachineType::AnyTagged(),
kFullWriteBarrier};
return access;
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -345,6 +345,7 @@ class V8_EXPORT_PRIVATE AccessBuilder final ...@@ -345,6 +345,7 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to a FeedbackVector fields. // Provides access to a FeedbackVector fields.
static FieldAccess ForFeedbackVectorClosureFeedbackCellArray(); static FieldAccess ForFeedbackVectorClosureFeedbackCellArray();
static FieldAccess ForFeedbackVectorOptimizedCodeWeakOrSmi();
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder); DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder);
......
...@@ -2923,9 +2923,9 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -2923,9 +2923,9 @@ void InstructionSelector::VisitTailCall(Node* node) {
auto call_descriptor = CallDescriptorOf(node->op()); auto call_descriptor = CallDescriptorOf(node->op());
CallDescriptor* caller = linkage()->GetIncomingDescriptor(); CallDescriptor* caller = linkage()->GetIncomingDescriptor();
DCHECK(caller->CanTailCall(CallDescriptorOf(node->op())));
const CallDescriptor* callee = CallDescriptorOf(node->op()); const CallDescriptor* callee = CallDescriptorOf(node->op());
int stack_param_delta = callee->GetStackParameterDelta(caller); DCHECK(caller->CanTailCall(callee));
const int stack_param_delta = callee->GetStackParameterDelta(caller);
CallBuffer buffer(zone(), call_descriptor, nullptr); CallBuffer buffer(zone(), call_descriptor, nullptr);
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
...@@ -2942,7 +2942,7 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -2942,7 +2942,7 @@ void InstructionSelector::VisitTailCall(Node* node) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
InstructionOperandVector temps(zone()); InstructionOperandVector temps(zone());
if (linkage()->GetIncomingDescriptor()->IsJSFunctionCall()) { if (caller->IsJSFunctionCall()) {
switch (call_descriptor->kind()) { switch (call_descriptor->kind()) {
case CallDescriptor::kCallCodeObject: case CallDescriptor::kCallCodeObject:
opcode = kArchTailCallCodeObjectFromJSFunction; opcode = kArchTailCallCodeObjectFromJSFunction;
...@@ -2980,7 +2980,7 @@ void InstructionSelector::VisitTailCall(Node* node) { ...@@ -2980,7 +2980,7 @@ void InstructionSelector::VisitTailCall(Node* node) {
// instruction. This is used by backends that need to pad arguments for stack // instruction. This is used by backends that need to pad arguments for stack
// alignment, in order to store an optional slot of padding above the // alignment, in order to store an optional slot of padding above the
// arguments. // arguments.
int optional_padding_slot = callee->GetFirstUnusedStackSlot(); const int optional_padding_slot = callee->GetFirstUnusedStackSlot();
buffer.instruction_args.push_back(g.TempImmediate(optional_padding_slot)); buffer.instruction_args.push_back(g.TempImmediate(optional_padding_slot));
const int first_unused_stack_slot = const int first_unused_stack_slot =
......
...@@ -657,13 +657,33 @@ void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, ...@@ -657,13 +657,33 @@ void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
namespace { namespace {
void AdjustStackPointerForTailCall(TurboAssembler* assembler, void AdjustStackPointerForTailCall(Instruction* instr,
TurboAssembler* assembler, Linkage* linkage,
OptimizedCompilationInfo* info,
FrameAccessState* state, FrameAccessState* state,
int new_slot_above_sp, int new_slot_above_sp,
bool allow_shrinkage = true) { bool allow_shrinkage = true) {
int current_sp_offset = state->GetSPToFPSlotCount() + int stack_slot_delta;
StandardFrameConstants::kFixedSlotCountAboveFp; if (HasCallDescriptorFlag(instr, CallDescriptor::kIsTailCallForTierUp)) {
int stack_slot_delta = new_slot_above_sp - current_sp_offset; // For this special tail-call mode, the callee has the same arguments and
// linkage as the caller, and arguments adapter frames must be preserved.
// Thus we simply have reset the stack pointer register to its original
// value before frame construction.
// See also: AssembleConstructFrame.
DCHECK(!info->is_osr());
DCHECK_EQ(linkage->GetIncomingDescriptor()->CalleeSavedRegisters(), 0);
DCHECK_EQ(linkage->GetIncomingDescriptor()->CalleeSavedFPRegisters(), 0);
DCHECK_EQ(state->frame()->GetReturnSlotCount(), 0);
stack_slot_delta = (state->frame()->GetTotalFrameSlotCount() -
kReturnAddressStackSlotCount) *
-1;
DCHECK_LE(stack_slot_delta, 0);
} else {
int current_sp_offset = state->GetSPToFPSlotCount() +
StandardFrameConstants::kFixedSlotCountAboveFp;
stack_slot_delta = new_slot_above_sp - current_sp_offset;
}
if (stack_slot_delta > 0) { if (stack_slot_delta > 0) {
assembler->AllocateStackSpace(stack_slot_delta * kSystemPointerSize); assembler->AllocateStackSpace(stack_slot_delta * kSystemPointerSize);
state->IncreaseSPDelta(stack_slot_delta); state->IncreaseSPDelta(stack_slot_delta);
...@@ -690,12 +710,14 @@ void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, ...@@ -690,12 +710,14 @@ void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
if (!pushes.empty() && if (!pushes.empty() &&
(LocationOperand::cast(pushes.back()->destination()).index() + 1 == (LocationOperand::cast(pushes.back()->destination()).index() + 1 ==
first_unused_stack_slot)) { first_unused_stack_slot)) {
DCHECK(!HasCallDescriptorFlag(instr, CallDescriptor::kIsTailCallForTierUp));
X64OperandConverter g(this, instr); X64OperandConverter g(this, instr);
for (auto move : pushes) { for (auto move : pushes) {
LocationOperand destination_location( LocationOperand destination_location(
LocationOperand::cast(move->destination())); LocationOperand::cast(move->destination()));
InstructionOperand source(move->source()); InstructionOperand source(move->source());
AdjustStackPointerForTailCall(tasm(), frame_access_state(), AdjustStackPointerForTailCall(instr, tasm(), linkage(), info(),
frame_access_state(),
destination_location.index()); destination_location.index());
if (source.IsStackSlot()) { if (source.IsStackSlot()) {
LocationOperand source_location(LocationOperand::cast(source)); LocationOperand source_location(LocationOperand::cast(source));
...@@ -713,14 +735,15 @@ void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, ...@@ -713,14 +735,15 @@ void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
move->Eliminate(); move->Eliminate();
} }
} }
AdjustStackPointerForTailCall(tasm(), frame_access_state(), AdjustStackPointerForTailCall(instr, tasm(), linkage(), info(),
first_unused_stack_slot, false); frame_access_state(), first_unused_stack_slot,
false);
} }
void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
int first_unused_stack_slot) { int first_unused_stack_slot) {
AdjustStackPointerForTailCall(tasm(), frame_access_state(), AdjustStackPointerForTailCall(instr, tasm(), linkage(), info(),
first_unused_stack_slot); frame_access_state(), first_unused_stack_slot);
} }
// Check that {kJavaScriptCallCodeStartRegister} is correct. // Check that {kJavaScriptCallCodeStartRegister} is correct.
...@@ -824,12 +847,13 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -824,12 +847,13 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
break; break;
} }
case kArchTailCallCodeObjectFromJSFunction: case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: { if (!HasCallDescriptorFlag(instr, CallDescriptor::kIsTailCallForTierUp)) {
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1), i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2)); i.TempRegister(2));
} }
V8_FALLTHROUGH;
case kArchTailCallCodeObject: {
if (HasImmediateInput(instr, 0)) { if (HasImmediateInput(instr, 0)) {
Handle<Code> code = i.InputCode(0); Handle<Code> code = i.InputCode(0);
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
...@@ -4372,7 +4396,7 @@ static const int kQuadWordSize = 16; ...@@ -4372,7 +4396,7 @@ static const int kQuadWordSize = 16;
} // namespace } // namespace
void CodeGenerator::FinishFrame(Frame* frame) { void CodeGenerator::FinishFrame(Frame* frame) {
auto call_descriptor = linkage()->GetIncomingDescriptor(); CallDescriptor* call_descriptor = linkage()->GetIncomingDescriptor();
const RegList saves_fp = call_descriptor->CalleeSavedFPRegisters(); const RegList saves_fp = call_descriptor->CalleeSavedFPRegisters();
if (saves_fp != 0) { if (saves_fp != 0) {
......
...@@ -39,7 +39,7 @@ class BytecodeGraphBuilder { ...@@ -39,7 +39,7 @@ class BytecodeGraphBuilder {
BailoutId osr_offset, JSGraph* jsgraph, BailoutId osr_offset, JSGraph* jsgraph,
CallFrequency const& invocation_frequency, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id, SourcePositionTable* source_positions, int inlining_id,
BytecodeGraphBuilderFlags flags, CodeKind code_kind, BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter); TickCounter* tick_counter);
// Creates a graph by visiting bytecodes. // Creates a graph by visiting bytecodes.
...@@ -63,8 +63,9 @@ class BytecodeGraphBuilder { ...@@ -63,8 +63,9 @@ class BytecodeGraphBuilder {
// Get or create the node that represents the outer function closure. // Get or create the node that represents the outer function closure.
Node* GetFunctionClosure(); Node* GetFunctionClosure();
CodeKind code_kind() const { return code_kind_; }
bool native_context_independent() const { bool native_context_independent() const {
return native_context_independent_; return CodeKindIsNativeContextIndependentJSFunction(code_kind_);
} }
// The node representing the current feedback vector is generated once prior // The node representing the current feedback vector is generated once prior
...@@ -97,6 +98,12 @@ class BytecodeGraphBuilder { ...@@ -97,6 +98,12 @@ class BytecodeGraphBuilder {
Node* BuildLoadFeedbackCell(int index); Node* BuildLoadFeedbackCell(int index);
// Checks the optimization marker and potentially triggers compilation or
// installs the finished code object.
// Only relevant for specific code kinds (see
// CodeKindChecksOptimizationMarker).
void MaybeBuildTierUpCheck();
// Builder for loading the a native context field. // Builder for loading the a native context field.
Node* BuildLoadNativeContextField(int index); Node* BuildLoadNativeContextField(int index);
...@@ -426,7 +433,7 @@ class BytecodeGraphBuilder { ...@@ -426,7 +433,7 @@ class BytecodeGraphBuilder {
int input_buffer_size_; int input_buffer_size_;
Node** input_buffer_; Node** input_buffer_;
const bool native_context_independent_; const CodeKind code_kind_;
Node* feedback_cell_node_; Node* feedback_cell_node_;
Node* feedback_vector_node_; Node* feedback_vector_node_;
Node* native_context_node_; Node* native_context_node_;
...@@ -958,7 +965,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -958,7 +965,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
SharedFunctionInfoRef const& shared_info, SharedFunctionInfoRef const& shared_info,
FeedbackVectorRef const& feedback_vector, BailoutId osr_offset, FeedbackVectorRef const& feedback_vector, BailoutId osr_offset,
JSGraph* jsgraph, CallFrequency const& invocation_frequency, JSGraph* jsgraph, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, int inlining_id, SourcePositionTable* source_positions, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags, TickCounter* tick_counter) BytecodeGraphBuilderFlags flags, TickCounter* tick_counter)
: broker_(broker), : broker_(broker),
local_zone_(local_zone), local_zone_(local_zone),
...@@ -997,8 +1004,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder( ...@@ -997,8 +1004,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
current_exception_handler_(0), current_exception_handler_(0),
input_buffer_size_(0), input_buffer_size_(0),
input_buffer_(nullptr), input_buffer_(nullptr),
native_context_independent_( code_kind_(code_kind),
flags & BytecodeGraphBuilderFlag::kNativeContextIndependent),
feedback_cell_node_(nullptr), feedback_cell_node_(nullptr),
feedback_vector_node_(nullptr), feedback_vector_node_(nullptr),
native_context_node_(nullptr), native_context_node_(nullptr),
...@@ -1120,6 +1126,19 @@ Node* BytecodeGraphBuilder::BuildLoadNativeContext() { ...@@ -1120,6 +1126,19 @@ Node* BytecodeGraphBuilder::BuildLoadNativeContext() {
return native_context; return native_context;
} }
void BytecodeGraphBuilder::MaybeBuildTierUpCheck() {
if (!CodeKindChecksOptimizationMarker(code_kind())) return;
Environment* env = environment();
Node* control = env->GetControlDependency();
Node* effect = env->GetEffectDependency();
effect = graph()->NewNode(simplified()->TierUpCheck(), feedback_vector_node(),
effect, control);
env->UpdateEffectDependency(effect);
}
Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) { Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
Node* result = NewNode(javascript()->LoadContext(0, index, true)); Node* result = NewNode(javascript()->LoadContext(0, index, true));
NodeProperties::ReplaceContextInput(result, native_context_node()); NodeProperties::ReplaceContextInput(result, native_context_node());
...@@ -1141,9 +1160,9 @@ void BytecodeGraphBuilder::CreateGraph() { ...@@ -1141,9 +1160,9 @@ void BytecodeGraphBuilder::CreateGraph() {
// Set up the basic structure of the graph. Outputs for {Start} are the formal // Set up the basic structure of the graph. Outputs for {Start} are the formal
// parameters (including the receiver) plus new target, number of arguments, // parameters (including the receiver) plus new target, number of arguments,
// context and closure. // context and closure.
int actual_parameter_count = StartNode::OutputArityForFormalParameterCount( int start_output_arity = StartNode::OutputArityForFormalParameterCount(
bytecode_array().parameter_count()); bytecode_array().parameter_count());
graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count))); graph()->SetStart(graph()->NewNode(common()->Start(start_output_arity)));
Environment env(this, bytecode_array().register_count(), Environment env(this, bytecode_array().register_count(),
bytecode_array().parameter_count(), bytecode_array().parameter_count(),
...@@ -1153,7 +1172,9 @@ void BytecodeGraphBuilder::CreateGraph() { ...@@ -1153,7 +1172,9 @@ void BytecodeGraphBuilder::CreateGraph() {
CreateFeedbackCellNode(); CreateFeedbackCellNode();
CreateFeedbackVectorNode(); CreateFeedbackVectorNode();
MaybeBuildTierUpCheck();
CreateNativeContextNode(); CreateNativeContextNode();
VisitBytecodes(); VisitBytecodes();
// Finish the basic structure of the graph. // Finish the basic structure of the graph.
...@@ -4402,13 +4423,14 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone, ...@@ -4402,13 +4423,14 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
BailoutId osr_offset, JSGraph* jsgraph, BailoutId osr_offset, JSGraph* jsgraph,
CallFrequency const& invocation_frequency, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, SourcePositionTable* source_positions,
int inlining_id, BytecodeGraphBuilderFlags flags, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter) { TickCounter* tick_counter) {
DCHECK(broker->IsSerializedForCompilation(shared_info, feedback_vector)); DCHECK(broker->IsSerializedForCompilation(shared_info, feedback_vector));
BytecodeGraphBuilder builder( BytecodeGraphBuilder builder(
broker, local_zone, broker->target_native_context(), shared_info, broker, local_zone, broker->target_native_context(), shared_info,
feedback_vector, osr_offset, jsgraph, invocation_frequency, feedback_vector, osr_offset, jsgraph, invocation_frequency,
source_positions, inlining_id, flags, tick_counter); source_positions, inlining_id, code_kind, flags, tick_counter);
builder.CreateGraph(); builder.CreateGraph();
} }
......
...@@ -7,8 +7,9 @@ ...@@ -7,8 +7,9 @@
#include "src/compiler/js-operator.h" #include "src/compiler/js-operator.h"
#include "src/compiler/js-type-hint-lowering.h" #include "src/compiler/js-type-hint-lowering.h"
#include "src/utils/utils.h"
#include "src/handles/handles.h" #include "src/handles/handles.h"
#include "src/objects/code-kind.h"
#include "src/utils/utils.h"
namespace v8 { namespace v8 {
...@@ -33,7 +34,6 @@ enum class BytecodeGraphBuilderFlag : uint8_t { ...@@ -33,7 +34,6 @@ enum class BytecodeGraphBuilderFlag : uint8_t {
// bytecode analysis. // bytecode analysis.
kAnalyzeEnvironmentLiveness = 1 << 1, kAnalyzeEnvironmentLiveness = 1 << 1,
kBailoutOnUninitialized = 1 << 2, kBailoutOnUninitialized = 1 << 2,
kNativeContextIndependent = 1 << 3,
}; };
using BytecodeGraphBuilderFlags = base::Flags<BytecodeGraphBuilderFlag>; using BytecodeGraphBuilderFlags = base::Flags<BytecodeGraphBuilderFlag>;
...@@ -45,7 +45,8 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone, ...@@ -45,7 +45,8 @@ void BuildGraphFromBytecode(JSHeapBroker* broker, Zone* local_zone,
BailoutId osr_offset, JSGraph* jsgraph, BailoutId osr_offset, JSGraph* jsgraph,
CallFrequency const& invocation_frequency, CallFrequency const& invocation_frequency,
SourcePositionTable* source_positions, SourcePositionTable* source_positions,
int inlining_id, BytecodeGraphBuilderFlags flags, int inlining_id, CodeKind code_kind,
BytecodeGraphBuilderFlags flags,
TickCounter* tick_counter); TickCounter* tick_counter);
} // namespace compiler } // namespace compiler
......
...@@ -179,7 +179,8 @@ class EffectControlLinearizer { ...@@ -179,7 +179,8 @@ class EffectControlLinearizer {
void LowerCheckEqualsInternalizedString(Node* node, Node* frame_state); void LowerCheckEqualsInternalizedString(Node* node, Node* frame_state);
void LowerCheckEqualsSymbol(Node* node, Node* frame_state); void LowerCheckEqualsSymbol(Node* node, Node* frame_state);
Node* LowerTypeOf(Node* node); Node* LowerTypeOf(Node* node);
Node* LowerUpdateInterruptBudget(Node* node); void LowerTierUpCheck(Node* node);
void LowerUpdateInterruptBudget(Node* node);
Node* LowerToBoolean(Node* node); Node* LowerToBoolean(Node* node);
Node* LowerPlainPrimitiveToNumber(Node* node); Node* LowerPlainPrimitiveToNumber(Node* node);
Node* LowerPlainPrimitiveToWord32(Node* node); Node* LowerPlainPrimitiveToWord32(Node* node);
...@@ -1140,8 +1141,11 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -1140,8 +1141,11 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kTypeOf: case IrOpcode::kTypeOf:
result = LowerTypeOf(node); result = LowerTypeOf(node);
break; break;
case IrOpcode::kTierUpCheck:
LowerTierUpCheck(node);
break;
case IrOpcode::kUpdateInterruptBudget: case IrOpcode::kUpdateInterruptBudget:
result = LowerUpdateInterruptBudget(node); LowerUpdateInterruptBudget(node);
break; break;
case IrOpcode::kNewDoubleElements: case IrOpcode::kNewDoubleElements:
result = LowerNewDoubleElements(node); result = LowerNewDoubleElements(node);
...@@ -3734,7 +3738,82 @@ Node* EffectControlLinearizer::LowerTypeOf(Node* node) { ...@@ -3734,7 +3738,82 @@ Node* EffectControlLinearizer::LowerTypeOf(Node* node) {
__ NoContextConstant()); __ NoContextConstant());
} }
Node* EffectControlLinearizer::LowerUpdateInterruptBudget(Node* node) { void EffectControlLinearizer::LowerTierUpCheck(Node* node) {
TierUpCheckNode n(node);
TNode<FeedbackVector> vector = n.feedback_vector();
Node* optimization_marker = __ LoadField(
AccessBuilder::ForFeedbackVectorOptimizedCodeWeakOrSmi(), vector);
// TODO(jgruber): The branch introduces a sequence of spills before the
// branch (and restores at `fallthrough`) that are completely unnecessary
// since the IfFalse continuation ends in a tail call. Investigate how to
// avoid these and fix it.
// TODO(jgruber): Combine the checks below for none/queued, e.g. by
// reorganizing OptimizationMarker values such that the least significant bit
// says whether the value is interesting or not. Also update the related
// check in the InterpreterEntryTrampoline.
auto fallthrough = __ MakeLabel();
auto optimization_marker_is_not_none = __ MakeDeferredLabel();
auto optimization_marker_is_neither_none_nor_queued = __ MakeDeferredLabel();
__ BranchWithHint(
__ TaggedEqual(optimization_marker, __ SmiConstant(static_cast<int>(
OptimizationMarker::kNone))),
&fallthrough, &optimization_marker_is_not_none, BranchHint::kTrue);
__ Bind(&optimization_marker_is_not_none);
__ BranchWithHint(
__ TaggedEqual(optimization_marker,
__ SmiConstant(static_cast<int>(
OptimizationMarker::kInOptimizationQueue))),
&fallthrough, &optimization_marker_is_neither_none_nor_queued,
BranchHint::kNone);
__ Bind(&optimization_marker_is_neither_none_nor_queued);
// The optimization marker field contains a non-trivial value, and some
// action has to be taken. For example, perhaps tier-up has been requested
// and we need to kick off a compilation job; or optimized code is available
// and should be tail-called.
//
// Currently we delegate these tasks to the InterpreterEntryTrampoline.
// TODO(jgruber,v8:8888): Consider a dedicated builtin instead.
const int parameter_count =
StartNode{graph()->start()}.FormalParameterCount();
TNode<HeapObject> code =
__ HeapConstant(BUILTIN_CODE(isolate(), InterpreterEntryTrampoline));
Node* target = __ Parameter(Linkage::kJSCallClosureParamIndex);
Node* new_target =
__ Parameter(Linkage::GetJSCallNewTargetParamIndex(parameter_count));
Node* argc =
__ Parameter(Linkage::GetJSCallArgCountParamIndex(parameter_count));
Node* context =
__ Parameter(Linkage::GetJSCallContextParamIndex(parameter_count));
JSTrampolineDescriptor descriptor;
CallDescriptor::Flags flags = CallDescriptor::kFixedTargetRegister |
CallDescriptor::kIsTailCallForTierUp;
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), descriptor, descriptor.GetStackParameterCount(), flags,
Operator::kNoProperties);
Node* nodes[] = {code, target, new_target, argc,
context, __ effect(), __ control()};
#ifdef DEBUG
static constexpr int kCodeContextEffectControl = 4;
DCHECK_EQ(arraysize(nodes),
descriptor.GetParameterCount() + kCodeContextEffectControl);
#endif // DEBUG
__ TailCall(call_descriptor, arraysize(nodes), nodes);
__ Bind(&fallthrough);
}
void EffectControlLinearizer::LowerUpdateInterruptBudget(Node* node) {
UpdateInterruptBudgetNode n(node); UpdateInterruptBudgetNode n(node);
TNode<FeedbackCell> feedback_cell = n.feedback_cell(); TNode<FeedbackCell> feedback_cell = n.feedback_cell();
TNode<Int32T> budget = __ LoadField<Int32T>( TNode<Int32T> budget = __ LoadField<Int32T>(
...@@ -3755,7 +3834,6 @@ Node* EffectControlLinearizer::LowerUpdateInterruptBudget(Node* node) { ...@@ -3755,7 +3834,6 @@ Node* EffectControlLinearizer::LowerUpdateInterruptBudget(Node* node) {
__ Bind(&next); __ Bind(&next);
} }
return nullptr;
} }
Node* EffectControlLinearizer::LowerToBoolean(Node* node) { Node* EffectControlLinearizer::LowerToBoolean(Node* node) {
......
...@@ -32,6 +32,7 @@ class GraphAssembler::BasicBlockUpdater { ...@@ -32,6 +32,7 @@ class GraphAssembler::BasicBlockUpdater {
void AddBranch(Node* branch, BasicBlock* tblock, BasicBlock* fblock); void AddBranch(Node* branch, BasicBlock* tblock, BasicBlock* fblock);
void AddGoto(BasicBlock* to); void AddGoto(BasicBlock* to);
void AddGoto(BasicBlock* from, BasicBlock* to); void AddGoto(BasicBlock* from, BasicBlock* to);
void AddTailCall(Node* node);
void StartBlock(BasicBlock* block); void StartBlock(BasicBlock* block);
BasicBlock* Finalize(BasicBlock* original); BasicBlock* Finalize(BasicBlock* original);
...@@ -267,6 +268,18 @@ void GraphAssembler::BasicBlockUpdater::AddGoto(BasicBlock* from, ...@@ -267,6 +268,18 @@ void GraphAssembler::BasicBlockUpdater::AddGoto(BasicBlock* from,
current_block_ = nullptr; current_block_ = nullptr;
} }
void GraphAssembler::BasicBlockUpdater::AddTailCall(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kTailCall);
DCHECK_NOT_NULL(current_block_);
if (state_ == kUnchanged) {
CopyForChange();
}
schedule_->AddTailCall(current_block_, node);
current_block_ = nullptr;
}
void GraphAssembler::BasicBlockUpdater::UpdateSuccessors(BasicBlock* block) { void GraphAssembler::BasicBlockUpdater::UpdateSuccessors(BasicBlock* block) {
for (SuccessorInfo succ : saved_successors_) { for (SuccessorInfo succ : saved_successors_) {
(succ.block->predecessors())[succ.index] = block; (succ.block->predecessors())[succ.index] = block;
...@@ -380,6 +393,11 @@ Node* GraphAssembler::ExternalConstant(ExternalReference ref) { ...@@ -380,6 +393,11 @@ Node* GraphAssembler::ExternalConstant(ExternalReference ref) {
return AddClonedNode(mcgraph()->ExternalConstant(ref)); return AddClonedNode(mcgraph()->ExternalConstant(ref));
} }
Node* GraphAssembler::Parameter(int index) {
return AddNode(
graph()->NewNode(common()->Parameter(index), graph()->start()));
}
Node* JSGraphAssembler::CEntryStubConstant(int result_size) { Node* JSGraphAssembler::CEntryStubConstant(int result_size) {
return AddClonedNode(jsgraph()->CEntryStubConstant(result_size)); return AddClonedNode(jsgraph()->CEntryStubConstant(result_size));
} }
...@@ -781,6 +799,28 @@ TNode<Object> GraphAssembler::Call(const Operator* op, int inputs_size, ...@@ -781,6 +799,28 @@ TNode<Object> GraphAssembler::Call(const Operator* op, int inputs_size,
return AddNode<Object>(graph()->NewNode(op, inputs_size, inputs)); return AddNode<Object>(graph()->NewNode(op, inputs_size, inputs));
} }
void GraphAssembler::TailCall(const CallDescriptor* call_descriptor,
int inputs_size, Node** inputs) {
#ifdef DEBUG
static constexpr int kTargetEffectControl = 3;
DCHECK_EQ(inputs_size,
call_descriptor->ParameterCount() + kTargetEffectControl);
#endif // DEBUG
Node* node = AddNode(graph()->NewNode(common()->TailCall(call_descriptor),
inputs_size, inputs));
if (block_updater_) block_updater_->AddTailCall(node);
// Unlike ConnectUnreachableToEnd, the TailCall node terminates a block; to
// keep it live, it *must* be connected to End (also in Turboprop schedules).
NodeProperties::MergeControlToEnd(graph(), common(), node);
// Setting effect, control to nullptr effectively terminates the current block
// by disallowing the addition of new nodes until a new label has been bound.
InitializeEffectControl(nullptr, nullptr);
}
void GraphAssembler::BranchWithCriticalSafetyCheck( void GraphAssembler::BranchWithCriticalSafetyCheck(
Node* condition, GraphAssemblerLabel<0u>* if_true, Node* condition, GraphAssemblerLabel<0u>* if_true,
GraphAssemblerLabel<0u>* if_false) { GraphAssemblerLabel<0u>* if_false) {
......
...@@ -240,6 +240,8 @@ class V8_EXPORT_PRIVATE GraphAssembler { ...@@ -240,6 +240,8 @@ class V8_EXPORT_PRIVATE GraphAssembler {
Node* Projection(int index, Node* value); Node* Projection(int index, Node* value);
Node* ExternalConstant(ExternalReference ref); Node* ExternalConstant(ExternalReference ref);
Node* Parameter(int index);
Node* LoadFramePointer(); Node* LoadFramePointer();
Node* LoadHeapNumberValue(Node* heap_number); Node* LoadHeapNumberValue(Node* heap_number);
...@@ -326,6 +328,8 @@ class V8_EXPORT_PRIVATE GraphAssembler { ...@@ -326,6 +328,8 @@ class V8_EXPORT_PRIVATE GraphAssembler {
Args... args); Args... args);
template <typename... Args> template <typename... Args>
TNode<Object> Call(const Operator* op, Node* first_arg, Args... args); TNode<Object> Call(const Operator* op, Node* first_arg, Args... args);
void TailCall(const CallDescriptor* call_descriptor, int inputs_size,
Node** inputs);
// Basic control operations. // Basic control operations.
template <size_t VarCount> template <size_t VarCount>
......
...@@ -470,8 +470,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -470,8 +470,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
CallFrequency frequency = call.frequency(); CallFrequency frequency = call.frequency();
BuildGraphFromBytecode(broker(), zone(), *shared_info, feedback_vector, BuildGraphFromBytecode(broker(), zone(), *shared_info, feedback_vector,
BailoutId::None(), jsgraph(), frequency, BailoutId::None(), jsgraph(), frequency,
source_positions_, inlining_id, flags, source_positions_, inlining_id, info_->code_kind(),
&info_->tick_counter()); flags, &info_->tick_counter());
} }
// Extract the inlinee start/end nodes. // Extract the inlinee start/end nodes.
......
...@@ -94,6 +94,12 @@ int CallDescriptor::GetFirstUnusedStackSlot() const { ...@@ -94,6 +94,12 @@ int CallDescriptor::GetFirstUnusedStackSlot() const {
int CallDescriptor::GetStackParameterDelta( int CallDescriptor::GetStackParameterDelta(
CallDescriptor const* tail_caller) const { CallDescriptor const* tail_caller) const {
// In the IsTailCallForTierUp case, the callee has
// identical linkage and runtime arguments to the caller, thus the stack
// parameter delta is 0. We don't explicitly pass the runtime arguments as
// inputs to the TailCall node, since they already exist on the stack.
if (IsTailCallForTierUp()) return 0;
int callee_slots_above_sp = GetFirstUnusedStackSlot(); int callee_slots_above_sp = GetFirstUnusedStackSlot();
int tail_caller_slots_above_sp = tail_caller->GetFirstUnusedStackSlot(); int tail_caller_slots_above_sp = tail_caller->GetFirstUnusedStackSlot();
int stack_param_delta = callee_slots_above_sp - tail_caller_slots_above_sp; int stack_param_delta = callee_slots_above_sp - tail_caller_slots_above_sp;
......
...@@ -226,13 +226,30 @@ class V8_EXPORT_PRIVATE CallDescriptor final ...@@ -226,13 +226,30 @@ class V8_EXPORT_PRIVATE CallDescriptor final
// The kCallerSavedFPRegisters only matters (and set) when the more general // The kCallerSavedFPRegisters only matters (and set) when the more general
// flag for kCallerSavedRegisters above is also set. // flag for kCallerSavedRegisters above is also set.
kCallerSavedFPRegisters = 1u << 8, kCallerSavedFPRegisters = 1u << 8,
// AIX has a function descriptor by default but it can be disabled for a // Tail calls for tier up are special (in fact they are different enough
// certain CFunction call (only used for Kind::kCallAddress). // from normal tail calls to warrant a dedicated opcode; but they also have
kNoFunctionDescriptor = 1u << 9, // enough similar aspects that reusing the TailCall opcode is pragmatic).
// Specifically:
//
// 1. Caller and callee are both JS-linkage Code objects.
// 2. JS runtime arguments are passed unchanged from caller to callee.
// 3. JS runtime arguments are not attached as inputs to the TailCall node.
// 4. Prior to the tail call, frame and register state is torn down to just
// before the caller frame was constructed.
// 5. Unlike normal tail calls, arguments adaptor frames (if present) are
// *not* torn down.
//
// In other words, behavior is identical to a jmp instruction prior caller
// frame construction.
kIsTailCallForTierUp = 1u << 9,
// Flags past here are *not* encoded in InstructionCode and are thus not // Flags past here are *not* encoded in InstructionCode and are thus not
// accessible from the code generator. See also // accessible from the code generator. See also
// kFlagsBitsEncodedInInstructionCode. // kFlagsBitsEncodedInInstructionCode.
// AIX has a function descriptor by default but it can be disabled for a
// certain CFunction call (only used for Kind::kCallAddress).
kNoFunctionDescriptor = 1u << 10,
}; };
using Flags = base::Flags<Flag>; using Flags = base::Flags<Flag>;
...@@ -331,6 +348,7 @@ class V8_EXPORT_PRIVATE CallDescriptor final ...@@ -331,6 +348,7 @@ class V8_EXPORT_PRIVATE CallDescriptor final
bool NeedsCallerSavedFPRegisters() const { bool NeedsCallerSavedFPRegisters() const {
return flags() & kCallerSavedFPRegisters; return flags() & kCallerSavedFPRegisters;
} }
bool IsTailCallForTierUp() const { return flags() & kIsTailCallForTierUp; }
bool NoFunctionDescriptor() const { return flags() & kNoFunctionDescriptor; } bool NoFunctionDescriptor() const { return flags() & kNoFunctionDescriptor; }
LinkageLocation GetReturnLocation(size_t index) const { LinkageLocation GetReturnLocation(size_t index) const {
......
...@@ -483,6 +483,7 @@ ...@@ -483,6 +483,7 @@
V(StringToLowerCaseIntl) \ V(StringToLowerCaseIntl) \
V(StringToNumber) \ V(StringToNumber) \
V(StringToUpperCaseIntl) \ V(StringToUpperCaseIntl) \
V(TierUpCheck) \
V(ToBoolean) \ V(ToBoolean) \
V(TransitionAndStoreElement) \ V(TransitionAndStoreElement) \
V(TransitionAndStoreNonNumberElement) \ V(TransitionAndStoreNonNumberElement) \
......
...@@ -1423,17 +1423,14 @@ struct GraphBuilderPhase { ...@@ -1423,17 +1423,14 @@ struct GraphBuilderPhase {
if (data->info()->bailout_on_uninitialized()) { if (data->info()->bailout_on_uninitialized()) {
flags |= BytecodeGraphBuilderFlag::kBailoutOnUninitialized; flags |= BytecodeGraphBuilderFlag::kBailoutOnUninitialized;
} }
if (data->info()->IsNativeContextIndependent()) {
flags |= BytecodeGraphBuilderFlag::kNativeContextIndependent;
}
JSFunctionRef closure(data->broker(), data->info()->closure()); JSFunctionRef closure(data->broker(), data->info()->closure());
CallFrequency frequency(1.0f); CallFrequency frequency(1.0f);
BuildGraphFromBytecode( BuildGraphFromBytecode(
data->broker(), temp_zone, closure.shared(), closure.feedback_vector(), data->broker(), temp_zone, closure.shared(), closure.feedback_vector(),
data->info()->osr_offset(), data->jsgraph(), frequency, data->info()->osr_offset(), data->jsgraph(), frequency,
data->source_positions(), SourcePosition::kNotInlined, flags, data->source_positions(), SourcePosition::kNotInlined,
&data->info()->tick_counter()); data->info()->code_kind(), flags, &data->info()->tick_counter());
} }
}; };
......
...@@ -2821,6 +2821,7 @@ class RepresentationSelector { ...@@ -2821,6 +2821,7 @@ class RepresentationSelector {
return VisitUnop<T>(node, UseInfo::AnyTagged(), return VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
} }
case IrOpcode::kTierUpCheck:
case IrOpcode::kUpdateInterruptBudget: { case IrOpcode::kUpdateInterruptBudget: {
ProcessInput<T>(node, 0, UseInfo::AnyTagged()); ProcessInput<T>(node, 0, UseInfo::AnyTagged());
ProcessRemainingInputs<T>(node, 1); ProcessRemainingInputs<T>(node, 1);
......
...@@ -1322,6 +1322,12 @@ const Operator* SimplifiedOperatorBuilder::UpdateInterruptBudget(int delta) { ...@@ -1322,6 +1322,12 @@ const Operator* SimplifiedOperatorBuilder::UpdateInterruptBudget(int delta) {
"UpdateInterruptBudget", 1, 1, 1, 0, 1, 0, delta); "UpdateInterruptBudget", 1, 1, 1, 0, 1, 0, delta);
} }
const Operator* SimplifiedOperatorBuilder::TierUpCheck() {
return zone()->New<Operator>(IrOpcode::kTierUpCheck,
Operator::kNoThrow | Operator::kNoDeopt,
"TierUpCheck", 1, 1, 1, 0, 1, 0);
}
const Operator* SimplifiedOperatorBuilder::AssertType(Type type) { const Operator* SimplifiedOperatorBuilder::AssertType(Type type) {
DCHECK(type.IsRange()); DCHECK(type.IsRange());
return zone()->New<Operator1<Type>>(IrOpcode::kAssertType, return zone()->New<Operator1<Type>>(IrOpcode::kAssertType,
......
...@@ -810,6 +810,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -810,6 +810,11 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
// delta parameter represents the executed bytecodes since the last update. // delta parameter represents the executed bytecodes since the last update.
const Operator* UpdateInterruptBudget(int delta); const Operator* UpdateInterruptBudget(int delta);
// Takes the current feedback vector as input 0, and generates a check of the
// vector's marker. Depending on the marker's value, we either do nothing,
// trigger optimized compilation, or install a finished code object.
const Operator* TierUpCheck();
const Operator* ToBoolean(); const Operator* ToBoolean();
const Operator* StringConcat(); const Operator* StringConcat();
...@@ -1165,6 +1170,18 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase { ...@@ -1165,6 +1170,18 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
} }
}; };
class TierUpCheckNode final : public SimplifiedNodeWrapperBase {
public:
explicit constexpr TierUpCheckNode(Node* node)
: SimplifiedNodeWrapperBase(node) {
CONSTEXPR_DCHECK(node->opcode() == IrOpcode::kTierUpCheck);
}
#define INPUTS(V) V(FeedbackVector, feedback_vector, 0, FeedbackVector)
INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS
};
class UpdateInterruptBudgetNode final : public SimplifiedNodeWrapperBase { class UpdateInterruptBudgetNode final : public SimplifiedNodeWrapperBase {
public: public:
explicit constexpr UpdateInterruptBudgetNode(Node* node) explicit constexpr UpdateInterruptBudgetNode(Node* node)
......
...@@ -396,7 +396,8 @@ UnobservablesSet RedundantStoreFinder::RecomputeUseIntersection(Node* node) { ...@@ -396,7 +396,8 @@ UnobservablesSet RedundantStoreFinder::RecomputeUseIntersection(Node* node) {
// Everything is observable after these opcodes; return the empty set. // Everything is observable after these opcodes; return the empty set.
DCHECK_EXTRA( DCHECK_EXTRA(
opcode == IrOpcode::kReturn || opcode == IrOpcode::kTerminate || opcode == IrOpcode::kReturn || opcode == IrOpcode::kTerminate ||
opcode == IrOpcode::kDeoptimize || opcode == IrOpcode::kThrow, opcode == IrOpcode::kDeoptimize || opcode == IrOpcode::kThrow ||
opcode == IrOpcode::kTailCall,
"for #%d:%s", node->id(), node->op()->mnemonic()); "for #%d:%s", node->id(), node->op()->mnemonic());
USE(opcode); USE(opcode);
......
...@@ -1196,6 +1196,7 @@ Type Typer::Visitor::TypeTypeOf(Node* node) { ...@@ -1196,6 +1196,7 @@ Type Typer::Visitor::TypeTypeOf(Node* node) {
return Type::InternalizedString(); return Type::InternalizedString();
} }
Type Typer::Visitor::TypeTierUpCheck(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeUpdateInterruptBudget(Node* node) { UNREACHABLE(); } Type Typer::Visitor::TypeUpdateInterruptBudget(Node* node) { UNREACHABLE(); }
// JS conversion operators. // JS conversion operators.
......
...@@ -763,6 +763,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -763,6 +763,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
case IrOpcode::kTypeOf: case IrOpcode::kTypeOf:
CheckTypeIs(node, Type::InternalizedString()); CheckTypeIs(node, Type::InternalizedString());
break; break;
case IrOpcode::kTierUpCheck:
case IrOpcode::kUpdateInterruptBudget: case IrOpcode::kUpdateInterruptBudget:
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckNotTyped(node); CheckNotTyped(node);
......
...@@ -80,12 +80,9 @@ inline constexpr bool CodeKindChecksOptimizationMarker(CodeKind kind) { ...@@ -80,12 +80,9 @@ inline constexpr bool CodeKindChecksOptimizationMarker(CodeKind kind) {
// The optimization marker field on the feedback vector has a dual purpose of // The optimization marker field on the feedback vector has a dual purpose of
// controlling the tier-up workflow, and caching the produced code object for // controlling the tier-up workflow, and caching the produced code object for
// access from multiple closures. The marker is not used for all code kinds // access from multiple closures. The marker is not used for all code kinds
// though, in particular it is not used when generating NCI code for caching // though, in particular it is not used when generating NCI code.
// only.
inline constexpr bool CodeKindIsStoredInOptimizedCodeCache(CodeKind kind) { inline constexpr bool CodeKindIsStoredInOptimizedCodeCache(CodeKind kind) {
return kind == CodeKind::OPTIMIZED_FUNCTION || return kind == CodeKind::OPTIMIZED_FUNCTION;
(FLAG_turbo_nci_as_midtier &&
kind == CodeKind::NATIVE_CONTEXT_INDEPENDENT);
} }
inline CodeKind CodeKindForTopTier() { return CodeKind::OPTIMIZED_FUNCTION; } inline CodeKind CodeKindForTopTier() { return CodeKind::OPTIMIZED_FUNCTION; }
......
...@@ -94,6 +94,11 @@ bool JSFunction::HasAvailableOptimizedCode() const { ...@@ -94,6 +94,11 @@ bool JSFunction::HasAvailableOptimizedCode() const {
return (result & kOptimizedJSFunctionCodeKindsMask) != 0; return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
} }
bool JSFunction::HasAvailableCodeKind(CodeKind kind) const {
CodeKinds result = GetAvailableCodeKinds();
return (result & CodeKindToCodeKindFlag(kind)) != 0;
}
namespace { namespace {
// Returns false if no highest tier exists (i.e. the function is not compiled), // Returns false if no highest tier exists (i.e. the function is not compiled),
......
...@@ -110,6 +110,8 @@ class JSFunction : public JSFunctionOrBoundFunction { ...@@ -110,6 +110,8 @@ class JSFunction : public JSFunctionOrBoundFunction {
V8_EXPORT_PRIVATE bool HasAttachedOptimizedCode() const; V8_EXPORT_PRIVATE bool HasAttachedOptimizedCode() const;
bool HasAvailableOptimizedCode() const; bool HasAvailableOptimizedCode() const;
bool HasAvailableCodeKind(CodeKind kind) const;
V8_EXPORT_PRIVATE bool ActiveTierIsIgnition() const; V8_EXPORT_PRIVATE bool ActiveTierIsIgnition() const;
bool ActiveTierIsTurbofan() const; bool ActiveTierIsTurbofan() const;
bool ActiveTierIsNCI() const; bool ActiveTierIsNCI() const;
......
...@@ -66,6 +66,8 @@ Object CompileOptimized(Isolate* isolate, Handle<JSFunction> function, ...@@ -66,6 +66,8 @@ Object CompileOptimized(Isolate* isolate, Handle<JSFunction> function,
return isolate->StackOverflow(); return isolate->StackOverflow();
} }
if (function->HasOptimizationMarker()) function->ClearOptimizationMarker();
if (!Compiler::CompileOptimized(function, mode, function->NextTier())) { if (!Compiler::CompileOptimized(function, mode, function->NextTier())) {
return ReadOnlyRoots(isolate).exception(); return ReadOnlyRoots(isolate).exception();
} }
......
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