Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Unified Diff: src/hydrogen.cc

Issue 55933002: Inline array constructor. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Comment response, inline tracing, and making HForceRepresentation idef Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/hydrogen.cc
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 2e05654ef82046552d6abb929ad7ede0f2f79089..31ee40945a632f46185169c173df3ffaebcd601a 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -1558,6 +1558,43 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
}
+
+HValue* HGraphBuilder::BuildAllocateArrayFromLength(
+ JSArrayBuilder* array_builder,
+ HValue* length_argument) {
+ if (length_argument->IsConstant() &&
+ HConstant::cast(length_argument)->HasSmiValue()) {
+ int array_length = HConstant::cast(length_argument)->Integer32Value();
+ HValue* new_object = (array_length == 0)
Toon Verwaest 2013/11/11 13:59:28 Remove the parentheses.
mvstanton 2013/11/13 14:12:52 Done.
+ ? array_builder->AllocateEmptyArray()
+ : array_builder->AllocateArray(length_argument, length_argument);
+ return new_object;
+ }
+
+ HValue* constant_zero = graph()->GetConstant0();
+ HConstant* max_alloc_length =
+ Add<HConstant>(JSObject::kInitialMaxFastElementArray);
+ const int initial_capacity = JSArray::kPreallocatedArrayElements;
+ HConstant* initial_capacity_node = Add<HConstant>(initial_capacity);
Toon Verwaest 2013/11/11 13:59:28 Move this down to the case where initial-capacity-
mvstanton 2013/11/13 14:12:52 Done.
+ HInstruction* checked_length = Add<HBoundsCheck>(length_argument,
+ max_alloc_length);
+ IfBuilder if_builder(this);
+ if_builder.If<HCompareNumericAndBranch>(checked_length, constant_zero,
+ Token::EQ);
+ if_builder.Then();
+ Push(initial_capacity_node); // capacity
+ Push(constant_zero); // length
+ if_builder.Else();
+ Push(checked_length); // capacity
+ Push(checked_length); // length
+ if_builder.End();
+
+ // Figure out total size
+ HValue* length = Pop();
+ HValue* capacity = Pop();
+ return array_builder->AllocateArray(capacity, length);
+}
+
HValue* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
HValue* capacity) {
int elements_size;
@@ -1746,19 +1783,20 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
: Add<HConstant>(nan_double);
// Special loop unfolding case
- static const int kLoopUnfoldLimit = 4;
+ static const int kLoopUnfoldLimit = 6;
bool unfold_loop = false;
- int initial_capacity = JSArray::kPreallocatedArrayElements;
- if (from->IsConstant() && to->IsConstant() &&
- initial_capacity <= kLoopUnfoldLimit) {
- HConstant* constant_from = HConstant::cast(from);
- HConstant* constant_to = HConstant::cast(to);
+ int initial_capacity;
+
+ if (from->ActualValue()->IsConstant() && to->ActualValue()->IsConstant()) {
Toon Verwaest 2013/11/11 13:59:28 Please put this is a separate CL (that also more w
mvstanton 2013/11/13 14:12:52 I made another CL that isolates the idef of HForce
+ HConstant* constant_from = HConstant::cast(from->ActualValue());
+ HConstant* constant_to = HConstant::cast(to->ActualValue());
if (constant_from->HasInteger32Value() &&
constant_from->Integer32Value() == 0 &&
constant_to->HasInteger32Value() &&
- constant_to->Integer32Value() == initial_capacity) {
+ constant_to->Integer32Value() <= kLoopUnfoldLimit) {
unfold_loop = true;
+ initial_capacity = constant_to->Integer32Value();
}
}
@@ -2004,11 +2042,13 @@ HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
ElementsKind kind,
HValue* allocation_site_payload,
HValue* constructor_function,
- AllocationSiteOverrideMode override_mode) :
+ AllocationSiteOverrideMode override_mode,
+ bool is_inlined) :
builder_(builder),
kind_(kind),
allocation_site_payload_(allocation_site_payload),
- constructor_function_(constructor_function) {
+ constructor_function_(constructor_function),
+ is_inlined_(is_inlined) {
mode_ = override_mode == DISABLE_ALLOCATION_SITES
? DONT_TRACK_ALLOCATION_SITE
: AllocationSite::GetMode(kind);
@@ -2022,11 +2062,19 @@ HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
kind_(kind),
mode_(DONT_TRACK_ALLOCATION_SITE),
allocation_site_payload_(NULL),
- constructor_function_(constructor_function) {
+ constructor_function_(constructor_function),
+ is_inlined_(false) {
}
HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
+ if (is_inlined_) {
Toon Verwaest 2013/11/11 13:59:28 Is it good enough to use IsStub() rather than is_i
mvstanton 2013/11/13 14:12:52 Sure is, thanks for the idea. Done.
+ // A constant map is fine.
+ Handle<Map> map(builder()->isolate()->get_initial_js_array_map(kind_),
+ builder()->isolate());
+ return builder()->Add<HConstant>(map);
+ }
+
if (kind_ == GetInitialFastElementsKind()) {
// No need for a context lookup if the kind_ matches the initial
// map, because we can just load the map in that case.
@@ -2067,6 +2115,16 @@ HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize(
STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize);
base_size += FixedArray::kHeaderSize;
+ if (length_node->IsConstant()) {
+ HConstant* constant_length_node = HConstant::cast(length_node);
+ if (constant_length_node->HasInteger32Value()) {
+ int size = base_size + elements_size() *
+ constant_length_node->Integer32Value();
+ HInstruction* const_size = builder()->Add<HConstant>(size);
+ return const_size;
Toon Verwaest 2013/11/11 13:59:28 I think constant folding should take care of this.
mvstanton 2013/11/13 14:12:52 Indeed, thx!
+ }
+ }
+
HInstruction* elements_size_value =
builder()->Add<HConstant>(elements_size());
HInstruction* mul = builder()->Add<HMul>(length_node, elements_size_value);
@@ -2098,23 +2156,22 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() {
HConstant* capacity = builder()->Add<HConstant>(initial_capacity());
return AllocateArray(size_in_bytes,
capacity,
- builder()->graph()->GetConstant0(),
- true);
+ builder()->graph()->GetConstant0());
}
HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity,
HValue* length_field,
- bool fill_with_hole) {
+ FillMode fill_mode) {
HValue* size_in_bytes = EstablishAllocationSize(capacity);
- return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole);
+ return AllocateArray(size_in_bytes, capacity, length_field, fill_mode);
}
HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
HValue* capacity,
HValue* length_field,
- bool fill_with_hole) {
+ FillMode fill_mode) {
// These HForceRepresentations are because we store these as fields in the
// objects we construct, and an int32-to-smi HChange could deopt. Accept
// the deopt possibility now, before allocation occurs.
@@ -2148,7 +2205,7 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
// Initialize the elements
builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity);
- if (fill_with_hole) {
+ if (fill_mode == FILL_WITH_HOLE) {
builder()->BuildFillElementsWithHole(elements_location_, kind_,
graph()->GetConstant0(), capacity);
}
@@ -7169,6 +7226,73 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
}
+void HOptimizedGraphBuilder::BuildInlinedCallNewArray(CallNew* expr) {
+ NoObservableSideEffectsScope no_effects(this);
+
+ int argument_count = expr->arguments()->length();
+ // We should at least have the constructor on the expression stack.
+ ASSERT(!environment()->ExpressionStackIsEmpty());
Toon Verwaest 2013/11/11 13:59:28 I this ASSERT and the comment necessary? Doesn't t
mvstanton 2013/11/13 14:12:52 Done.
+ HValue* constructor = environment()->ExpressionStackAt(argument_count);
+
+ ElementsKind kind = expr->elements_kind();
+ Handle<Cell> cell = expr->allocation_info_cell();
+ AllocationSite* site = AllocationSite::cast(cell->value());
+
+ // Register on the site for deoptimization if the cell value changes.
+ site->AddDependentCompilationInfo(AllocationSite::TRANSITIONS, top_info());
+ HInstruction* cell_instruction = Add<HConstant>(cell);
+
+ // In the single constant argument case, we may have to adjust elements kind
+ // to avoid creating a packed non-empty array (not kosher).
Toon Verwaest 2013/11/11 13:59:28 I'd remove the (not kosher) part.
mvstanton 2013/11/13 14:12:52 Done.
+ if (argument_count == 1 && !IsHoleyElementsKind(kind)) {
Toon Verwaest 2013/11/11 13:59:28 Can't we just do this in BuildAllocateArrayFromLen
mvstanton 2013/11/13 14:12:52 Hmm, I'd rather not because then the JSArrayBuilde
+ HValue* argument = environment()->Top();
+ if (argument->IsConstant()) {
+ HConstant* constant_argument = HConstant::cast(argument);
+ ASSERT(constant_argument->HasSmiValue());
+ int constant_array_size = constant_argument->Integer32Value();
+ if (constant_array_size != 0) {
+ kind = GetHoleyElementsKind(kind);
+ }
+ }
+ }
+
+ // Build the array.
+ JSArrayBuilder array_builder(this,
+ kind,
+ cell_instruction,
+ constructor,
+ DISABLE_ALLOCATION_SITES,
+ true);
+ HValue* new_object;
+ if (argument_count == 0) {
+ new_object = array_builder.AllocateEmptyArray();
+ } else if (argument_count == 1) {
+ HValue* argument = environment()->Top();
+ new_object = BuildAllocateArrayFromLength(&array_builder, argument);
+ } else {
+ HValue* length = Add<HConstant>(argument_count);
+ // Smi arrays need to initialize array elements with the hole because
+ // bailout could occur if the arguments don't fit in a smi.
+ //
+ // TODO(mvstanton): If all the arguments are constants in smi range, then
+ // we could set fill_with_hole to false and save a few instructions.
+ JSArrayBuilder::FillMode fill_mode = IsFastSmiElementsKind(kind)
+ ? JSArrayBuilder::FILL_WITH_HOLE
+ : JSArrayBuilder::DONT_FILL_WITH_HOLE;
+ new_object = array_builder.AllocateArray(length, length, fill_mode);
+ HValue* elements = array_builder.GetElementsLocation();
+ for (int i = 0; i < argument_count; i++) {
+ HValue* value = environment()->ExpressionStackAt(argument_count - i - 1);
+ HValue* constant_i = Add<HConstant>(i);
+ Add<HStoreKeyed>(elements, constant_i, value, kind);
+ }
+ }
+
+ Drop(argument_count + 1); // drop constructor and args.
+ ast_context()->ReturnValue(new_object);
+}
+
+
// Checks whether allocation using the given constructor can be inlined.
static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
return constructor->has_initial_map() &&
@@ -7178,6 +7302,57 @@ static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
}
+bool HOptimizedGraphBuilder::IsCallNewArrayInlineable(CallNew* expr) {
+ bool inline_ok = false;
+ Handle<JSFunction> caller = current_info()->closure();
+ Handle<JSFunction> target(isolate()->global_context()->array_function(),
+ isolate());
+ int argument_count = expr->arguments()->length();
+ // We should have the function plus array arguments on the environment stack.
+ ASSERT(environment()->length() >= (argument_count + 1));
+ Handle<Cell> cell = expr->allocation_info_cell();
+ AllocationSite* site = AllocationSite::cast(cell->value());
+ if (site->CanInlineCall()) {
+ // We also want to avoid inlining in certain 1 argument scenarios.
+ if (argument_count == 1) {
+ HValue* argument = Top();
+ if (argument->IsConstant()) {
+ // Do not inline if the constant length argument is not a smi or
+ // outside the valid range for a fast array.
+ HConstant* constant_argument = HConstant::cast(argument);
+ if (constant_argument->HasSmiValue()) {
+ int value = constant_argument->Integer32Value();
+ inline_ok = value >= 0 &&
+ value < JSObject::kInitialMaxFastElementArray;
+ if (!inline_ok) {
+ TraceInline(target, caller,
+ "Length outside of valid array range");
+ }
+ }
+ } else {
+ // The amount of code we'd have to generate doubles if we have a
+ // non-constant length, but packed elements_kind feedback. Don't
+ // inline this case.
+ inline_ok = IsHoleyElementsKind(expr->elements_kind());
Toon Verwaest 2013/11/11 13:59:28 Shouldn't we just deopt if the passed length is no
mvstanton 2013/11/13 14:12:52 Yep, done. I also added a test in array-constructo
+ if (!inline_ok) {
+ TraceInline(target, caller,
+ "Non-holey elements kind for single argument");
+ }
+ }
+ } else {
+ inline_ok = true;
+ }
+ } else {
+ TraceInline(target, caller, "AllocationSite requested no inlining.");
+ }
+
+ if (inline_ok) {
+ TraceInline(target, caller, NULL);
+ }
+ return inline_ok;
+}
+
+
void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
@@ -7186,14 +7361,15 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
int argument_count = expr->arguments()->length() + 1; // Plus constructor.
Factory* factory = isolate()->factory();
+ // The constructor function is on the stack in the unoptimized code
+ // during evaluation of the arguments.
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* function = Top();
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+
if (FLAG_inline_construct &&
expr->IsMonomorphic() &&
IsAllocationInlineable(expr->target())) {
- // The constructor function is on the stack in the unoptimized code
- // during evaluation of the arguments.
- CHECK_ALIVE(VisitForValue(expr->expression()));
- HValue* function = Top();
- CHECK_ALIVE(VisitExpressions(expr->arguments()));
Handle<JSFunction> constructor = expr->target();
HValue* check = Add<HCheckValue>(function, constructor);
@@ -7280,19 +7456,24 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
// argument to the construct call.
Handle<JSFunction> array_function(
isolate()->global_context()->array_function(), isolate());
- CHECK_ALIVE(VisitArgument(expr->expression()));
- HValue* constructor = HPushArgument::cast(Top())->argument();
- CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+ bool use_call_new_array = expr->target().is_identical_to(array_function);
+ Handle<Cell> cell = expr->allocation_info_cell();
+ if (use_call_new_array && IsCallNewArrayInlineable(expr)) {
+ // Verify we are still calling the array function for our native context.
+ Add<HCheckValue>(function, array_function);
+ BuildInlinedCallNewArray(expr);
+ return;
+ }
+
HBinaryCall* call;
- if (expr->target().is_identical_to(array_function)) {
- Handle<Cell> cell = expr->allocation_info_cell();
- Add<HCheckValue>(constructor, array_function);
- call = New<HCallNewArray>(constructor, argument_count,
- cell, expr->elements_kind());
+ if (use_call_new_array) {
+ Add<HCheckValue>(function, array_function);
+ call = New<HCallNewArray>(function, argument_count, cell,
+ expr->elements_kind());
} else {
- call = New<HCallNew>(constructor, argument_count);
+ call = New<HCallNew>(function, argument_count);
}
- Drop(argument_count);
+ PreProcessCall(call);
return ast_context()->ReturnInstruction(call, expr->id());
}
}

Powered by Google App Engine
This is Rietveld 408576698