Add Crankshaft support for smi-only elements

Review URL: http://codereview.chromium.org/8002019

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9426 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
jkummerow@chromium.org 2011-09-26 12:09:04 +00:00
parent 6dee868e03
commit 0455aadbeb
9 changed files with 208 additions and 80 deletions

View File

@ -3346,6 +3346,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
Register scratch = scratch0();
// This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
// conversion, so it deopts in that case.
if (instr->hydrogen()->ValueNeedsSmiCheck()) {
__ tst(value, Operand(kSmiTagMask));
DeoptimizeIf(ne, instr->environment());
}
// Do the store.
if (instr->key()->IsConstantOperand()) {
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());

View File

@ -908,8 +908,8 @@ class HDeoptimize: public HControlInstruction {
class HGoto: public HTemplateControlInstruction<1, 0> {
public:
explicit HGoto(HBasicBlock* target) {
SetSuccessorAt(0, target);
}
SetSuccessorAt(0, target);
}
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
@ -3740,7 +3740,9 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
class HStoreKeyedFastElement: public HTemplateInstruction<3> {
public:
HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val) {
HStoreKeyedFastElement(HValue* obj, HValue* key, HValue* val,
ElementsKind elements_kind = FAST_ELEMENTS)
: elements_kind_(elements_kind) {
SetOperandAt(0, obj);
SetOperandAt(1, key);
SetOperandAt(2, val);
@ -3757,14 +3759,28 @@ class HStoreKeyedFastElement: public HTemplateInstruction<3> {
HValue* object() { return OperandAt(0); }
HValue* key() { return OperandAt(1); }
HValue* value() { return OperandAt(2); }
bool value_is_smi() {
return elements_kind_ == FAST_SMI_ONLY_ELEMENTS;
}
bool NeedsWriteBarrier() {
return StoringValueNeedsWriteBarrier(value());
if (value_is_smi()) {
return false;
} else {
return StoringValueNeedsWriteBarrier(value());
}
}
bool ValueNeedsSmiCheck() {
return value_is_smi();
}
virtual void PrintDataTo(StringStream* stream);
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement)
private:
ElementsKind elements_kind_;
};

View File

@ -3347,7 +3347,42 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
Representation::Integer32()));
if (FLAG_smi_only_arrays) {
HInstruction* elements_kind =
AddInstruction(new(zone()) HElementsKind(literal));
HBasicBlock* store_fast = graph()->CreateBasicBlock();
// Two empty blocks to satisfy edge split form.
HBasicBlock* store_fast_edgesplit1 = graph()->CreateBasicBlock();
HBasicBlock* store_fast_edgesplit2 = graph()->CreateBasicBlock();
HBasicBlock* store_generic = graph()->CreateBasicBlock();
HBasicBlock* check_smi_only_elements = graph()->CreateBasicBlock();
HBasicBlock* join = graph()->CreateBasicBlock();
HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(value);
smicheck->SetSuccessorAt(0, store_fast_edgesplit1);
smicheck->SetSuccessorAt(1, check_smi_only_elements);
current_block()->Finish(smicheck);
store_fast_edgesplit1->Finish(new(zone()) HGoto(store_fast));
set_current_block(check_smi_only_elements);
HCompareConstantEqAndBranch* smi_elements_check =
new(zone()) HCompareConstantEqAndBranch(elements_kind,
FAST_SMI_ONLY_ELEMENTS,
Token::EQ_STRICT);
smi_elements_check->SetSuccessorAt(0, store_generic);
smi_elements_check->SetSuccessorAt(1, store_fast_edgesplit2);
current_block()->Finish(smi_elements_check);
store_fast_edgesplit2->Finish(new(zone()) HGoto(store_fast));
set_current_block(store_fast);
AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
store_fast->Goto(join);
set_current_block(store_generic);
AddInstruction(BuildStoreKeyedGeneric(literal, key, value));
store_generic->Goto(join);
join->SetJoinId(expr->id());
set_current_block(join);
} else {
AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
}
@ -3979,6 +4014,30 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
}
HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
HValue* checked_key,
HValue* val,
ElementsKind elements_kind,
bool is_store) {
if (is_store) {
ASSERT(val != NULL);
if (elements_kind == FAST_DOUBLE_ELEMENTS) {
return new(zone()) HStoreKeyedFastDoubleElement(
elements, checked_key, val);
} else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS.
return new(zone()) HStoreKeyedFastElement(
elements, checked_key, val, elements_kind);
}
}
// It's an element load (!is_store).
if (elements_kind == FAST_DOUBLE_ELEMENTS) {
return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
} else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS.
return new(zone()) HLoadKeyedFastElement(elements, checked_key);
}
}
HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
HValue* key,
HValue* val,
@ -3988,15 +4047,18 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
Handle<Map> map = expr->GetMonomorphicReceiverType();
AddInstruction(new(zone()) HCheckNonSmi(object));
HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
if (!map->has_fast_elements() &&
!map->has_fast_double_elements() &&
bool fast_smi_only_elements = map->has_fast_smi_only_elements();
bool fast_elements = map->has_fast_elements();
bool fast_double_elements = map->has_fast_double_elements();
if (!fast_smi_only_elements &&
!fast_elements &&
!fast_double_elements &&
!map->has_external_array_elements()) {
return is_store ? BuildStoreKeyedGeneric(object, key, val)
: BuildLoadKeyedGeneric(object, key);
}
HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
bool fast_double_elements = map->has_fast_double_elements();
if (is_store && map->has_fast_elements()) {
if (is_store && (fast_elements || fast_smi_only_elements)) {
AddInstruction(new(zone()) HCheckMap(
elements, isolate()->factory()->fixed_array_map()));
}
@ -4011,28 +4073,15 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
return BuildExternalArrayElementAccess(external_elements, checked_key,
val, map->elements_kind(), is_store);
}
ASSERT(map->has_fast_elements() || fast_double_elements);
ASSERT(fast_smi_only_elements || fast_elements || fast_double_elements);
if (map->instance_type() == JS_ARRAY_TYPE) {
length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck));
} else {
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
}
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
if (is_store) {
if (fast_double_elements) {
return new(zone()) HStoreKeyedFastDoubleElement(elements,
checked_key,
val);
} else {
return new(zone()) HStoreKeyedFastElement(elements, checked_key, val);
}
} else {
if (fast_double_elements) {
return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key);
} else {
return new(zone()) HLoadKeyedFastElement(elements, checked_key);
}
}
return BuildFastElementAccess(elements, checked_key, val,
map->elements_kind(), is_store);
}
@ -4085,7 +4134,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
elements_kind <= LAST_ELEMENTS_KIND;
elements_kind = ElementsKind(elements_kind + 1)) {
// After having handled FAST_ELEMENTS, FAST_SMI_ELEMENTS,
// After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS,
// FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code
// that's executed for all external array cases.
STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
@ -4112,13 +4161,22 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_DOUBLE_ELEMENTS) {
bool fast_double_elements =
elements_kind == FAST_DOUBLE_ELEMENTS;
if (is_store && !fast_double_elements) {
if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
AddInstruction(new(zone()) HCheckSmi(val));
}
if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) {
AddInstruction(new(zone()) HCheckMap(
elements, isolate()->factory()->fixed_array_map(),
elements_kind_branch));
}
// TODO(jkummerow): The need for these two blocks could be avoided
// in one of two ways:
// (1) Introduce ElementsKinds for JSArrays that are distinct from
// those for fast objects.
// (2) Put the common instructions into a third "join" block. This
// requires additional AST IDs that we can deopt to from inside
// that join block. They must be added to the Property class (when
// it's a keyed property) and registered in the full codegen.
HBasicBlock* if_jsarray = graph()->CreateBasicBlock();
HBasicBlock* if_fastobject = graph()->CreateBasicBlock();
HHasInstanceTypeAndBranch* typecheck =
@ -4129,37 +4187,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
set_current_block(if_jsarray);
HInstruction* length;
if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
// For now, fall back to the generic stub for
// FAST_SMI_ONLY_ELEMENTS
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
} else {
length = new(zone()) HJSArrayLength(object, typecheck);
AddInstruction(length);
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
if (is_store) {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HStoreKeyedFastDoubleElement(elements,
checked_key,
val));
} else {
access = AddInstruction(
new(zone()) HStoreKeyedFastElement(elements,
checked_key,
val));
}
} else {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HLoadKeyedFastDoubleElement(elements,
checked_key));
} else {
access = AddInstruction(
new(zone()) HLoadKeyedFastElement(elements, checked_key));
}
Push(access);
}
length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
access = AddInstruction(BuildFastElementAccess(
elements, checked_key, val, elements_kind, is_store));
if (!is_store) {
Push(access);
}
*has_side_effects |= access->HasSideEffects();
@ -4171,25 +4204,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
set_current_block(if_fastobject);
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
if (is_store) {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HStoreKeyedFastDoubleElement(elements,
checked_key,
val));
} else {
access = AddInstruction(
new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
}
} else {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
} else {
access = AddInstruction(
new(zone()) HLoadKeyedFastElement(elements, checked_key));
}
}
access = AddInstruction(BuildFastElementAccess(
elements, checked_key, val, elements_kind, is_store));
} else if (elements_kind == DICTIONARY_ELEMENTS) {
if (is_store) {
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));

View File

@ -942,6 +942,11 @@ class HGraphBuilder: public AstVisitor {
HValue* val,
ElementsKind elements_kind,
bool is_store);
HInstruction* BuildFastElementAccess(HValue* elements,
HValue* checked_key,
HValue* val,
ElementsKind elements_kind,
bool is_store);
HInstruction* BuildMonomorphicElementAccess(HValue* object,
HValue* key,

View File

@ -3238,6 +3238,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
Register elements = ToRegister(instr->object());
Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
// This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
// conversion, so it deopts in that case.
if (instr->hydrogen()->ValueNeedsSmiCheck()) {
__ test(value, Immediate(kSmiTagMask));
DeoptimizeIf(not_zero, instr->environment());
}
// Do the store.
if (instr->key()->IsConstantOperand()) {
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());

View File

@ -1689,6 +1689,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
Code* generic_stub) {
Code* result = NULL;
if (receiver->HasFastElements() ||
receiver->HasFastSmiOnlyElements() ||
receiver->HasExternalArrayElements() ||
receiver->HasFastDoubleElements() ||
receiver->HasDictionaryElements()) {

View File

@ -443,7 +443,7 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
for (int i = 0; i < content->length(); i++) {
Object* current = content->get(i);
ASSERT(!current->IsFixedArray());
if (!current->IsSmi()) {
if (!current->IsSmi() && !current->IsTheHole()) {
has_non_smi = true;
}
}
@ -467,7 +467,7 @@ static Handle<Object> CreateArrayLiteralBoilerplate(
content->set(i, *result);
has_non_smi = true;
} else {
if (!current->IsSmi()) {
if (!current->IsSmi() && !current->IsTheHole()) {
has_non_smi = true;
}
}
@ -8250,6 +8250,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
// The least significant bit (after untagging) indicates whether the
// function is currently optimized, regardless of reason.
if (!V8::UseCrankshaft()) {
return Smi::FromInt(4); // 4 == "never".
}

View File

@ -3161,6 +3161,13 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
Register elements = ToRegister(instr->object());
Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
// This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
// conversion, so it deopts in that case.
if (instr->hydrogen()->ValueNeedsSmiCheck()) {
Condition cc = masm()->CheckSmi(value);
DeoptimizeIf(NegateCondition(cc), instr->environment());
}
// Do the store.
if (instr->key()->IsConstantOperand()) {
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());

View File

@ -125,3 +125,70 @@ assertKind(element_kind.external_unsigned_int_elements, new Uint32Array(23));
assertKind(element_kind.external_float_elements, new Float32Array(7));
assertKind(element_kind.external_double_elements, new Float64Array(0));
assertKind(element_kind.external_pixel_elements, new PixelArray(512));
// Crankshaft support for smi-only array elements.
function monomorphic(array) {
for (var i = 0; i < 3; i++) {
array[i] = i + 10;
}
assertKind(element_kind.fast_smi_only_elements, array);
for (var i = 0; i < 3; i++) {
var a = array[i];
assertEquals(i + 10, a);
}
}
var smi_only = [1, 2, 3];
for (var i = 0; i < 3; i++) monomorphic(smi_only);
%OptimizeFunctionOnNextCall(monomorphic);
monomorphic(smi_only);
function polymorphic(array, expected_kind) {
array[1] = 42;
assertKind(expected_kind, array);
var a = array[1];
assertEquals(42, a);
}
var smis = [1, 2, 3];
var strings = ["one", "two", "three"];
var doubles = [0, 0, 0]; doubles[0] = 1.5; doubles[1] = 2.5; doubles[2] = 3.5;
assertKind(support_smi_only_arrays
? element_kind.fast_double_elements
: element_kind.fast_elements,
doubles);
for (var i = 0; i < 3; i++) {
polymorphic(smis, element_kind.fast_smi_only_elements);
polymorphic(strings, element_kind.fast_elements);
polymorphic(doubles, support_smi_only_arrays
? element_kind.fast_double_elements
: element_kind.fast_elements);
}
%OptimizeFunctionOnNextCall(polymorphic);
polymorphic(smis, element_kind.fast_smi_only_elements);
polymorphic(strings, element_kind.fast_elements);
polymorphic(doubles, support_smi_only_arrays
? element_kind.fast_double_elements
: element_kind.fast_elements);
// Crankshaft support for smi-only elements in dynamic array literals.
function get(foo) { return foo; } // Used to generate dynamic values.
//function crankshaft_test(expected_kind) {
function crankshaft_test() {
var a = [get(1), get(2), get(3)];
assertKind(element_kind.fast_smi_only_elements, a);
var b = [get(1), get(2), get("three")];
assertKind(element_kind.fast_elements, b);
var c = [get(1), get(2), get(3.5)];
// The full code generator doesn't support conversion to fast_double_elements
// yet. Crankshaft does, but only with --smi-only-arrays support.
if ((%GetOptimizationStatus(crankshaft_test) & 1) &&
support_smi_only_arrays) {
assertKind(element_kind.fast_double_elements, c);
} else {
assertKind(element_kind.fast_elements, c);
}
}
for (var i = 0; i < 3; i++) {
crankshaft_test();
}
%OptimizeFunctionOnNextCall(crankshaft_test);
crankshaft_test();