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:
jarin 2017-02-10 07:19:19 -08:00 committed by Commit bot
parent 0a9d4a3b0c
commit 36ed494784
17 changed files with 79 additions and 82 deletions

View File

@ -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(

View File

@ -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) \

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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());

View File

@ -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.

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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) \

View File

@ -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;
}

View File

@ -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(

View File

@ -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 = "";

View File

@ -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;

View File

@ -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