From 12b4e0efe718d23cadad2656abdbb27e20b9e760 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Wed, 16 Feb 2011 13:31:12 +0000 Subject: [PATCH] Implement crankshaft support for pixel array stores. Review URL: http://codereview.chromium.org/6528013 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6817 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 7 +++++++ src/hydrogen-instructions.cc | 14 ++++++++++++- src/hydrogen-instructions.h | 38 ++++++++++++++++++++++++++++++++++ src/hydrogen.cc | 44 +++++++++++++++++++++++++++++++++------- src/hydrogen.h | 5 +++++ src/ia32/lithium-codegen-ia32.cc | 25 ++++++++++++++++++++--- src/ia32/lithium-ia32.cc | 17 ++++++++++++++++ src/ia32/lithium-ia32.h | 23 +++++++++++++++++++++ src/x64/lithium-codegen-x64.cc | 18 ++++++++++++++++ src/x64/lithium-x64.cc | 14 +++++++++++++ src/x64/lithium-x64.h | 21 +++++++++++++++++++ test/cctest/test-api.cc | 18 ++++++++++++++++ 12 files changed, 233 insertions(+), 11 deletions(-) diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 903f77b..786ceeb 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1806,6 +1806,13 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( } +LInstruction* LChunkBuilder::DoStorePixelArrayElement( + HStorePixelArrayElement* instr) { + Abort("DoStorePixelArrayElement not implemented"); + return NULL; +} + + LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LOperand* obj = UseFixed(instr->object(), r2); LOperand* key = UseFixed(instr->key(), r1); diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 5accc77..c59372a 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -57,10 +57,13 @@ const char* Representation::Mnemonic() const { case kTagged: return "t"; case kDouble: return "d"; case kInteger32: return "i"; - default: + case kExternal: return "x"; + case kNumRepresentations: UNREACHABLE(); return NULL; } + UNREACHABLE(); + return NULL; } @@ -1202,6 +1205,15 @@ void HStoreKeyed::PrintDataTo(StringStream* stream) const { } +void HStorePixelArrayElement::PrintDataTo(StringStream* stream) const { + external_pointer()->PrintNameTo(stream); + stream->Add("["); + key()->PrintNameTo(stream); + stream->Add("] = "); + value()->PrintNameTo(stream); +} + + void HLoadGlobal::PrintDataTo(StringStream* stream) const { stream->Add("[%p]", *cell()); if (check_hole_value()) stream->Add(" (deleteable/read-only)"); diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 9f5170c..b121335 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -151,6 +151,7 @@ class LChunkBuilder; V(StoreContextSlot) \ V(StoreGlobal) \ V(StoreKeyedFastElement) \ + V(StorePixelArrayElement) \ V(StoreKeyedGeneric) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ @@ -3182,6 +3183,43 @@ class HStoreKeyedFastElement: public HStoreKeyed { }; +class HStorePixelArrayElement: public HInstruction { + public: + HStorePixelArrayElement(HValue* external_elements, HValue* key, HValue* val) { + SetFlag(kChangesPixelArrayElements); + SetOperandAt(0, external_elements); + SetOperandAt(1, key); + SetOperandAt(2, val); + } + + virtual void PrintDataTo(StringStream* stream) const; + virtual int OperandCount() const { return operands_.length(); } + virtual HValue* OperandAt(int index) const { return operands_[index]; } + + virtual Representation RequiredInputRepresentation(int index) const { + if (index == 0) { + return Representation::External(); + } else { + return Representation::Integer32(); + } + } + + HValue* external_pointer() const { return operands_[0]; } + HValue* key() const { return operands_[1]; } + HValue* value() const { return operands_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(StorePixelArrayElement, + "store_pixel_array_element") + + protected: + virtual void InternalSetOperandAt(int index, HValue* value) { + operands_[index] = value; + } + + HOperandVector<3> operands_; +}; + + class HStoreKeyedGeneric: public HStoreKeyed { public: HStoreKeyedGeneric(HValue* context, diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 3ebd580..0e27c07 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -3326,12 +3326,20 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { HValue* key = Pop(); HValue* object = Pop(); - bool is_fast_elements = expr->IsMonomorphic() && - expr->GetMonomorphicReceiverType()->has_fast_elements(); - - instr = is_fast_elements - ? BuildStoreKeyedFastElement(object, key, value, expr) - : BuildStoreKeyedGeneric(object, key, value); + if (expr->IsMonomorphic()) { + Handle receiver_type(expr->GetMonomorphicReceiverType()); + // An object has either fast elements or pixel array elements, but never + // both. Pixel array maps that are assigned to pixel array elements are + // always created with the fast elements flag cleared. + if (receiver_type->has_pixel_array_elements()) { + instr = BuildStoreKeyedPixelArrayElement(object, key, value, expr); + } else if (receiver_type->has_fast_elements()) { + instr = BuildStoreKeyedFastElement(object, key, value, expr); + } + } + if (instr == NULL) { + instr = BuildStoreKeyedGeneric(object, key, value); + } } Push(value); @@ -3711,7 +3719,8 @@ HInstruction* HGraphBuilder::BuildLoadKeyedPixelArrayElement(HValue* object, AddInstruction(new HCheckMap(object, map)); HLoadElements* elements = new HLoadElements(object); AddInstruction(elements); - HInstruction* length = AddInstruction(new HPixelArrayLength(elements)); + HInstruction* length = new HPixelArrayLength(elements); + AddInstruction(length); AddInstruction(new HBoundsCheck(key, length)); HLoadPixelArrayExternalPointer* external_elements = new HLoadPixelArrayExternalPointer(elements); @@ -3754,6 +3763,27 @@ HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object, } +HInstruction* HGraphBuilder::BuildStoreKeyedPixelArrayElement(HValue* object, + HValue* key, + HValue* val, + Expression* expr) { + ASSERT(expr->IsMonomorphic()); + AddInstruction(new HCheckNonSmi(object)); + Handle map = expr->GetMonomorphicReceiverType(); + ASSERT(!map->has_fast_elements()); + ASSERT(map->has_pixel_array_elements()); + AddInstruction(new HCheckMap(object, map)); + HLoadElements* elements = new HLoadElements(object); + AddInstruction(elements); + HInstruction* length = AddInstruction(new HPixelArrayLength(elements)); + AddInstruction(new HBoundsCheck(key, length)); + HLoadPixelArrayExternalPointer* external_elements = + new HLoadPixelArrayExternalPointer(elements); + AddInstruction(external_elements); + return new HStorePixelArrayElement(external_elements, key, val); +} + + bool HGraphBuilder::TryArgumentsAccess(Property* expr) { VariableProxy* proxy = expr->obj()->AsVariableProxy(); if (proxy == NULL) return false; diff --git a/src/hydrogen.h b/src/hydrogen.h index c911b6c..6d287ef 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -816,6 +816,11 @@ class HGraphBuilder: public AstVisitor { HValue* val, Expression* expr); + HInstruction* BuildStoreKeyedPixelArrayElement(HValue* object, + HValue* key, + HValue* val, + Expression* expr); + HCompare* BuildSwitchCompare(HSubgraph* subgraph, HValue* switch_value, CaseClause* clause); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index a59b1a5..ca72c97 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2100,13 +2100,13 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { void LCodeGen::DoLoadPixelArrayElement(LLoadPixelArrayElement* instr) { - Register external_elements = ToRegister(instr->external_pointer()); + Register external_pointer = ToRegister(instr->external_pointer()); Register key = ToRegister(instr->key()); Register result = ToRegister(instr->result()); - ASSERT(result.is(external_elements)); + ASSERT(result.is(external_pointer)); // Load the result. - __ movzx_b(result, Operand(external_elements, key, times_1, 0)); + __ movzx_b(result, Operand(external_pointer, key, times_1, 0)); } @@ -2731,6 +2731,25 @@ void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { } +void LCodeGen::DoStorePixelArrayElement(LStorePixelArrayElement* instr) { + Register external_pointer = ToRegister(instr->external_pointer()); + Register key = ToRegister(instr->key()); + Register value = ToRegister(instr->value()); + ASSERT(ToRegister(instr->TempAt(0)).is(eax)); + + __ mov(eax, value); + { // Clamp the value to [0..255]. + NearLabel done; + __ test(eax, Immediate(0xFFFFFF00)); + __ j(zero, &done); + __ setcc(negative, eax); // 1 if negative, 0 if positive. + __ dec_b(eax); // 0 if negative, 255 if positive. + __ bind(&done); + } + __ mov_b(Operand(external_pointer, key, times_1, 0), eax); +} + + void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { Register value = ToRegister(instr->value()); Register elements = ToRegister(instr->object()); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index a57e8c9..84fd08b 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1840,6 +1840,23 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( } +LInstruction* LChunkBuilder::DoStorePixelArrayElement( + HStorePixelArrayElement* instr) { + ASSERT(instr->value()->representation().IsInteger32()); + ASSERT(instr->external_pointer()->representation().IsExternal()); + ASSERT(instr->key()->representation().IsInteger32()); + + LOperand* external_pointer = UseRegister(instr->external_pointer()); + LOperand* val = UseRegister(instr->value()); + LOperand* key = UseRegister(instr->key()); + // The generated code requires that the clamped value is in a byte + // register. eax is an arbitrary choice to satisfy this requirement. + LOperand* clamped = FixedTemp(eax); + + return new LStorePixelArrayElement(external_pointer, key, val, clamped); +} + + LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* object = UseFixed(instr->object(), edx); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index f1b9ffc..2acd0c1 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -150,6 +150,7 @@ class LCodeGen; V(StoreKeyedGeneric) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ + V(StorePixelArrayElement) \ V(StringCharCodeAt) \ V(StringLength) \ V(SubI) \ @@ -1662,6 +1663,28 @@ class LStoreKeyedFastElement: public LStoreKeyed { }; +class LStorePixelArrayElement: public LTemplateInstruction<0, 3, 1> { + public: + LStorePixelArrayElement(LOperand* external_pointer, + LOperand* key, + LOperand* val, + LOperand* clamped) { + inputs_[0] = external_pointer; + inputs_[1] = key; + inputs_[2] = val; + temps_[0] = clamped; + } + + DECLARE_CONCRETE_INSTRUCTION(StorePixelArrayElement, + "store-pixel-array-element") + DECLARE_HYDROGEN_ACCESSOR(StorePixelArrayElement) + + LOperand* external_pointer() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } +}; + + class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> { public: LStoreKeyedGeneric(LOperand* context, diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index e1ebb3e..48d149e 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2148,6 +2148,24 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { } +void LCodeGen::DoStorePixelArrayElement(LStorePixelArrayElement* instr) { + Register external_pointer = ToRegister(instr->external_pointer()); + Register key = ToRegister(instr->key()); + Register value = ToRegister(instr->value()); + + { // Clamp the value to [0..255]. + NearLabel done; + __ testl(value, Immediate(0xFFFFFF00)); + __ j(zero, &done); + __ setcc(negative, value); // 1 if negative, 0 if positive. + __ decb(value); // 0 if negative, 255 if positive. + __ bind(&done); + } + + __ movb(Operand(external_pointer, key, times_1, 0), value); +} + + void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { if (instr->length()->IsRegister()) { __ cmpq(ToRegister(instr->index()), ToRegister(instr->length())); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index fba29a6..5b14639 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1716,6 +1716,20 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement( } +LInstruction* LChunkBuilder::DoStorePixelArrayElement( + HStorePixelArrayElement* instr) { + ASSERT(instr->value()->representation().IsInteger32()); + ASSERT(instr->external_pointer()->representation().IsExternal()); + ASSERT(instr->key()->representation().IsInteger32()); + + LOperand* external_pointer = UseRegister(instr->external_pointer()); + LOperand* val = UseTempRegister(instr->value()); + LOperand* key = UseRegister(instr->key()); + + return new LStorePixelArrayElement(external_pointer, key, val); +} + + LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { Abort("Unimplemented: %s", "DoStoreKeyedGeneric"); return NULL; diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index abffe50..80963d0 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -146,6 +146,7 @@ class LCodeGen; V(StoreKeyedGeneric) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ + V(StorePixelArrayElement) \ V(StringLength) \ V(SubI) \ V(TaggedToI) \ @@ -1542,6 +1543,26 @@ class LStoreKeyedFastElement: public LStoreKeyed { }; +class LStorePixelArrayElement: public LTemplateInstruction<0, 3, 0> { + public: + LStorePixelArrayElement(LOperand* external_pointer, + LOperand* key, + LOperand* val) { + inputs_[0] = external_pointer; + inputs_[1] = key; + inputs_[2] = val; + } + + DECLARE_CONCRETE_INSTRUCTION(StorePixelArrayElement, + "store-pixel-array-element") + DECLARE_HYDROGEN_ACCESSOR(StorePixelArrayElement) + + LOperand* external_pointer() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } +}; + + class LStoreKeyedGeneric: public LStoreKeyed { public: LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val) diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 3de5b92..c43078d 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -10825,6 +10825,24 @@ THREADED_TEST(PixelArray) { "result"); CHECK_EQ(32640, result->Int32Value()); + // Make sure that pixel array stores are optimized by crankshaft. + result = CompileRun("function pa_init(p) {" + "for (var i = 0; i < 256; ++i) { p[i] = i; }" + "}" + "function pa_load(p) {" + " var sum = 0;" + " for (var i=0; i<256; ++i) {" + " sum += p[i];" + " }" + " return sum; " + "}" + "for (var i = 0; i < 100000; ++i) {" + " pa_init(pixels);" + "}" + "result = pa_load(pixels);" + "result"); + CHECK_EQ(32640, result->Int32Value()); + free(pixel_data); } -- 2.7.4