Count closures using the feedback vector cell map, specialize if count==1.
This introduces new maps to track whether we have created at most one closure. If we have created just one closure, Turbofan will specialize the code to its context. Review-Url: https://codereview.chromium.org/2680313002 Cr-Commit-Position: refs/heads/master@{#43108}
This commit is contained in:
parent
0a9d4a3b0c
commit
36ed494784
@ -126,6 +126,26 @@ Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
|
||||
empty_fixed_array);
|
||||
Node* literals_cell = LoadFixedArrayElement(
|
||||
feedback_vector, slot, 0, CodeStubAssembler::SMI_PARAMETERS);
|
||||
{
|
||||
// Bump the closure counter encoded in the cell's map.
|
||||
Node* cell_map = LoadMap(literals_cell);
|
||||
Label no_closures(this), one_closure(this), cell_done(this);
|
||||
|
||||
GotoIf(IsNoClosuresCellMap(cell_map), &no_closures);
|
||||
GotoIf(IsOneClosureCellMap(cell_map), &one_closure);
|
||||
CSA_ASSERT(this, IsManyClosuresCellMap(cell_map));
|
||||
Goto(&cell_done);
|
||||
|
||||
Bind(&no_closures);
|
||||
StoreMapNoWriteBarrier(literals_cell, Heap::kOneClosureCellMapRootIndex);
|
||||
Goto(&cell_done);
|
||||
|
||||
Bind(&one_closure);
|
||||
StoreMapNoWriteBarrier(literals_cell, Heap::kManyClosuresCellMapRootIndex);
|
||||
Goto(&cell_done);
|
||||
|
||||
Bind(&cell_done);
|
||||
}
|
||||
StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackVectorOffset,
|
||||
literals_cell);
|
||||
StoreObjectFieldNoWriteBarrier(
|
||||
|
@ -34,6 +34,9 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
|
||||
V(FixedDoubleArrayMap, FixedDoubleArrayMap) \
|
||||
V(FunctionTemplateInfoMap, FunctionTemplateInfoMap) \
|
||||
V(HeapNumberMap, HeapNumberMap) \
|
||||
V(NoClosuresCellMap, NoClosuresCellMap) \
|
||||
V(OneClosureCellMap, OneClosureCellMap) \
|
||||
V(ManyClosuresCellMap, ManyClosuresCellMap) \
|
||||
V(MinusZeroValue, MinusZero) \
|
||||
V(NanValue, Nan) \
|
||||
V(NullValue, Null) \
|
||||
|
@ -210,8 +210,6 @@ Reduction JSCreateLowering::Reduce(Node* node) {
|
||||
return ReduceJSCreateArguments(node);
|
||||
case IrOpcode::kJSCreateArray:
|
||||
return ReduceJSCreateArray(node);
|
||||
case IrOpcode::kJSCreateClosure:
|
||||
return ReduceJSCreateClosure(node);
|
||||
case IrOpcode::kJSCreateIterResultObject:
|
||||
return ReduceJSCreateIterResultObject(node);
|
||||
case IrOpcode::kJSCreateKeyValueArray:
|
||||
@ -752,48 +750,6 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
|
||||
return ReduceNewArrayToStubCall(node, site);
|
||||
}
|
||||
|
||||
Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
|
||||
if (!FLAG_turbo_lower_create_closure) return NoChange();
|
||||
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
|
||||
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
|
||||
Handle<SharedFunctionInfo> shared = p.shared_info();
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
Node* context = NodeProperties::GetContextInput(node);
|
||||
|
||||
int const function_map_index =
|
||||
Context::FunctionMapIndex(shared->language_mode(), shared->kind());
|
||||
Node* function_map = jsgraph()->HeapConstant(
|
||||
handle(Map::cast(native_context()->get(function_map_index)), isolate()));
|
||||
|
||||
FeedbackSlot slot = p.feedback().slot();
|
||||
Node* literals_cell = jsgraph()->HeapConstant(
|
||||
handle(Cell::cast(p.feedback().vector()->Get(slot)), isolate()));
|
||||
|
||||
// Note that it is only safe to embed the raw entry point of the compile
|
||||
// lazy stub into the code, because that stub is immortal and immovable.
|
||||
Node* compile_entry = jsgraph()->PointerConstant(
|
||||
jsgraph()->isolate()->builtins()->CompileLazy()->entry());
|
||||
Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
|
||||
Node* the_hole = jsgraph()->TheHoleConstant();
|
||||
Node* undefined = jsgraph()->UndefinedConstant();
|
||||
AllocationBuilder a(jsgraph(), effect, control);
|
||||
STATIC_ASSERT(JSFunction::kSize == 9 * kPointerSize);
|
||||
a.Allocate(JSFunction::kSize, p.pretenure());
|
||||
a.Store(AccessBuilder::ForMap(), function_map);
|
||||
a.Store(AccessBuilder::ForJSObjectProperties(), empty_fixed_array);
|
||||
a.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
|
||||
a.Store(AccessBuilder::ForJSFunctionFeedbackVector(), literals_cell);
|
||||
a.Store(AccessBuilder::ForJSFunctionPrototypeOrInitialMap(), the_hole);
|
||||
a.Store(AccessBuilder::ForJSFunctionSharedFunctionInfo(), shared);
|
||||
a.Store(AccessBuilder::ForJSFunctionContext(), context);
|
||||
a.Store(AccessBuilder::ForJSFunctionCodeEntry(), compile_entry);
|
||||
a.Store(AccessBuilder::ForJSFunctionNextFunctionLink(), undefined);
|
||||
RelaxControls(node);
|
||||
a.FinishAndChange(node);
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode());
|
||||
Node* value = NodeProperties::GetValueInput(node, 0);
|
||||
|
@ -50,7 +50,6 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
|
||||
Reduction ReduceJSCreate(Node* node);
|
||||
Reduction ReduceJSCreateArguments(Node* node);
|
||||
Reduction ReduceJSCreateArray(Node* node);
|
||||
Reduction ReduceJSCreateClosure(Node* node);
|
||||
Reduction ReduceJSCreateIterResultObject(Node* node);
|
||||
Reduction ReduceJSCreateKeyValueArray(Node* node);
|
||||
Reduction ReduceJSCreateLiteral(Node* node);
|
||||
|
@ -594,6 +594,10 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl() {
|
||||
if (FLAG_inline_accessors) {
|
||||
info()->MarkAsAccessorInliningEnabled();
|
||||
}
|
||||
if (info()->closure()->feedback_vector_cell()->map() ==
|
||||
isolate()->heap()->one_closure_cell_map()) {
|
||||
info()->MarkAsFunctionContextSpecializing();
|
||||
}
|
||||
}
|
||||
if (!info()->is_optimizing_from_bytecode()) {
|
||||
if (!Compiler::EnsureDeoptimizationSupport(info())) return FAILED;
|
||||
|
@ -761,7 +761,9 @@ class FeedbackVectorFixer {
|
||||
|
||||
for (int i = 0; i < function_instances->length(); i++) {
|
||||
Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
|
||||
fun->set_feedback_vector_cell(isolate->heap()->undefined_cell());
|
||||
Handle<Cell> new_cell = isolate->factory()->NewManyClosuresCell(
|
||||
isolate->factory()->undefined_value());
|
||||
fun->set_feedback_vector_cell(*new_cell);
|
||||
// Only create feedback vectors if we already have the metadata.
|
||||
if (shared_info->is_compiled()) JSFunction::EnsureLiterals(fun);
|
||||
}
|
||||
|
@ -1173,6 +1173,24 @@ Handle<Cell> Factory::NewCell(Handle<Object> value) {
|
||||
Cell);
|
||||
}
|
||||
|
||||
Handle<Cell> Factory::NewNoClosuresCell(Handle<Object> value) {
|
||||
Handle<Cell> cell = NewCell(value);
|
||||
cell->set_map_no_write_barrier(*no_closures_cell_map());
|
||||
return cell;
|
||||
}
|
||||
|
||||
Handle<Cell> Factory::NewOneClosureCell(Handle<Object> value) {
|
||||
Handle<Cell> cell = NewCell(value);
|
||||
cell->set_map_no_write_barrier(*one_closure_cell_map());
|
||||
return cell;
|
||||
}
|
||||
|
||||
Handle<Cell> Factory::NewManyClosuresCell(Handle<Object> value) {
|
||||
Handle<Cell> cell = NewCell(value);
|
||||
cell->set_map_no_write_barrier(*many_closures_cell_map());
|
||||
return cell;
|
||||
}
|
||||
|
||||
Handle<PropertyCell> Factory::NewPropertyCell() {
|
||||
CALL_HEAP_FUNCTION(
|
||||
isolate(),
|
||||
@ -1590,6 +1608,15 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
|
||||
Handle<JSFunction> result =
|
||||
NewFunction(initial_map, info, context_or_undefined, pretenure);
|
||||
|
||||
// Bump the closure count that is encoded in the vector cell's map.
|
||||
if (vector->map() == *no_closures_cell_map()) {
|
||||
vector->set_map(*one_closure_cell_map());
|
||||
} else if (vector->map() == *one_closure_cell_map()) {
|
||||
vector->set_map(*many_closures_cell_map());
|
||||
} else {
|
||||
DCHECK_EQ(vector->map(), *many_closures_cell_map());
|
||||
}
|
||||
|
||||
result->set_feedback_vector_cell(*vector);
|
||||
if (info->ic_age() != isolate()->heap()->global_ic_age()) {
|
||||
info->ResetForNewContext(isolate()->heap()->global_ic_age());
|
||||
|
@ -373,6 +373,10 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
|
||||
Handle<WeakCell> NewWeakCell(Handle<HeapObject> value);
|
||||
|
||||
Handle<Cell> NewNoClosuresCell(Handle<Object> value);
|
||||
Handle<Cell> NewOneClosureCell(Handle<Object> value);
|
||||
Handle<Cell> NewManyClosuresCell(Handle<Object> value);
|
||||
|
||||
Handle<TransitionArray> NewTransitionArray(int capacity);
|
||||
|
||||
// Allocate a tenured AllocationSite. It's payload is null.
|
||||
|
@ -197,7 +197,7 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate,
|
||||
array->set(index, Smi::kZero, SKIP_WRITE_BARRIER);
|
||||
break;
|
||||
case FeedbackSlotKind::kCreateClosure: {
|
||||
Handle<Cell> cell = factory->NewCell(undefined_value);
|
||||
Handle<Cell> cell = factory->NewNoClosuresCell(undefined_value);
|
||||
array->set(index, *cell);
|
||||
break;
|
||||
}
|
||||
|
@ -494,8 +494,6 @@ DEFINE_BOOL(turbo_stress_instruction_scheduling, false,
|
||||
"randomly schedule instructions to stress dependency tracking")
|
||||
DEFINE_BOOL(turbo_store_elimination, true,
|
||||
"enable store-store elimination in TurboFan")
|
||||
DEFINE_BOOL(turbo_lower_create_closure, false,
|
||||
"enable inline allocation for closure instantiation")
|
||||
// TODO(turbofan): Rename --crankshaft to --optimize eventually.
|
||||
DEFINE_IMPLICATION(turbo, crankshaft)
|
||||
|
||||
|
@ -2330,6 +2330,9 @@ bool Heap::CreateInitialMaps() {
|
||||
ALLOCATE_MAP(CELL_TYPE, Cell::kSize, cell)
|
||||
ALLOCATE_MAP(PROPERTY_CELL_TYPE, PropertyCell::kSize, global_property_cell)
|
||||
ALLOCATE_MAP(WEAK_CELL_TYPE, WeakCell::kSize, weak_cell)
|
||||
ALLOCATE_MAP(CELL_TYPE, Cell::kSize, no_closures_cell)
|
||||
ALLOCATE_MAP(CELL_TYPE, Cell::kSize, one_closure_cell)
|
||||
ALLOCATE_MAP(CELL_TYPE, Cell::kSize, many_closures_cell)
|
||||
ALLOCATE_MAP(FILLER_TYPE, kPointerSize, one_pointer_filler)
|
||||
ALLOCATE_MAP(FILLER_TYPE, 2 * kPointerSize, two_pointer_filler)
|
||||
|
||||
|
@ -93,6 +93,9 @@ using v8::MemoryPressureLevel;
|
||||
V(Map, external_map, ExternalMap) \
|
||||
V(Map, bytecode_array_map, BytecodeArrayMap) \
|
||||
V(Map, module_info_map, ModuleInfoMap) \
|
||||
V(Map, no_closures_cell_map, NoClosuresCellMap) \
|
||||
V(Map, one_closure_cell_map, OneClosureCellMap) \
|
||||
V(Map, many_closures_cell_map, ManyClosuresCellMap) \
|
||||
/* String maps */ \
|
||||
V(Map, native_source_string_map, NativeSourceStringMap) \
|
||||
V(Map, string_map, StringMap) \
|
||||
@ -319,6 +322,9 @@ using v8::MemoryPressureLevel;
|
||||
V(ArgumentsMarkerMap) \
|
||||
V(JSMessageObjectMap) \
|
||||
V(ForeignMap) \
|
||||
V(NoClosuresCellMap) \
|
||||
V(OneClosureCellMap) \
|
||||
V(ManyClosuresCellMap) \
|
||||
V(NanValue) \
|
||||
V(InfinityValue) \
|
||||
V(MinusZeroValue) \
|
||||
|
@ -12156,7 +12156,8 @@ void JSFunction::EnsureLiterals(Handle<JSFunction> function) {
|
||||
// A top level script didn't get it's literals installed.
|
||||
Handle<FeedbackVector> feedback_vector =
|
||||
FeedbackVector::New(isolate, shared);
|
||||
Handle<Cell> new_cell = isolate->factory()->NewCell(feedback_vector);
|
||||
Handle<Cell> new_cell =
|
||||
isolate->factory()->NewOneClosureCell(feedback_vector);
|
||||
function->set_feedback_vector_cell(*new_cell);
|
||||
break;
|
||||
}
|
||||
|
@ -385,10 +385,12 @@ TEST(OptimizedCodeSharing1) {
|
||||
" return function() { return x; };"
|
||||
"}"
|
||||
"var closure0 = MakeClosure();"
|
||||
"var closure1 = MakeClosure();" // We only share optimized code
|
||||
// if there are at least two closures.
|
||||
"%DebugPrint(closure0());"
|
||||
"%OptimizeFunctionOnNextCall(closure0);"
|
||||
"%DebugPrint(closure0());"
|
||||
"var closure1 = MakeClosure(); closure1();"
|
||||
"closure1();"
|
||||
"var closure2 = MakeClosure(); closure2();");
|
||||
Handle<JSFunction> fun1 = Handle<JSFunction>::cast(
|
||||
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
|
||||
|
@ -25,10 +25,10 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --noalways-opt
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
|
||||
function TestCase(test_scenario, expected_output) {
|
||||
// Global variable, accessed from eval'd script.
|
||||
test_output = "";
|
||||
|
@ -25,6 +25,8 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --noalways-opt
|
||||
|
||||
|
||||
var Debug = debug.Debug;
|
||||
|
||||
|
@ -141,36 +141,6 @@ TEST_F(JSCreateLoweringTest, JSCreateArgumentsInlinedRestArray) {
|
||||
IsFinishRegion(IsAllocate(IsNumberConstant(JSArray::kSize), _, _), _));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSCreateClosure
|
||||
|
||||
TEST_F(JSCreateLoweringTest, JSCreateClosureViaInlinedAllocation) {
|
||||
if (!FLAG_turbo_lower_create_closure) return;
|
||||
Node* const context = UndefinedConstant();
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
Handle<SharedFunctionInfo> shared(isolate()->number_function()->shared());
|
||||
|
||||
// Create a mock feedback vector. It just has to be an array with an array
|
||||
// in slot 0.
|
||||
Handle<FixedArray> array = isolate()->factory()->NewFixedArray(
|
||||
FeedbackVector::kReservedIndexCount + 1);
|
||||
array->set_map_no_write_barrier(isolate()->heap()->feedback_vector_map());
|
||||
Handle<FeedbackVector> vector = Handle<FeedbackVector>::cast(array);
|
||||
FeedbackSlot slot(0);
|
||||
vector->Set(slot, *vector);
|
||||
VectorSlotPair pair(vector, slot);
|
||||
|
||||
Reduction r = Reduce(
|
||||
graph()->NewNode(javascript()->CreateClosure(shared, pair, NOT_TENURED),
|
||||
context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(),
|
||||
IsFinishRegion(IsAllocate(IsNumberConstant(JSFunction::kSize),
|
||||
IsBeginRegion(_), control),
|
||||
_));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSCreateFunctionContext
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user