From e9cc78af7ec7d9504da478b9e1679e86df690b0a Mon Sep 17 00:00:00 2001 From: "mvstanton@chromium.org" Date: Mon, 29 Jul 2013 11:50:39 +0000 Subject: [PATCH] Fix for V8 issue 2795: Check fails with deopt for mjsunit/array-store-and-grow (https://code.google.com/p/v8/issues/detail?id=2795) The reason is when allocating and building arrays in hydrogen we need to ensure we do any int32-to-smi conversions BEFORE the allocation. These conversions can at least theoretically deoptimize. If this happens before all the fields of the newly allocated object are filled in, we will have a corrupted heap. BUG= R=verwaest@chromium.org Review URL: https://codereview.chromium.org/20726002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15929 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/hydrogen-instructions.h | 14 ++++++++------ src/hydrogen.cc | 24 ++++++++++++++++++++---- test/mjsunit/array-store-and-grow.js | 22 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index dc312e0..0b81b87 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -5562,9 +5562,11 @@ class HObjectAccess { static HObjectAccess ForArrayLength(ElementsKind elements_kind) { return HObjectAccess( - kArrayLengths, JSArray::kLengthOffset, - IsFastElementsKind(elements_kind) && FLAG_track_fields ? - Representation::Smi() : Representation::Tagged()); + kArrayLengths, + JSArray::kLengthOffset, + IsFastElementsKind(elements_kind) && + FLAG_track_fields + ? Representation::Smi() : Representation::Tagged()); } static HObjectAccess ForAllocationSiteTransitionInfo() { @@ -5577,9 +5579,9 @@ class HObjectAccess { static HObjectAccess ForFixedArrayLength() { return HObjectAccess( - kArrayLengths, FixedArray::kLengthOffset, - FLAG_track_fields ? - Representation::Smi() : Representation::Tagged()); + kArrayLengths, + FixedArray::kLengthOffset, + FLAG_track_fields ? Representation::Smi() : Representation::Tagged()); } static HObjectAccess ForPropertiesPointer() { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index e346880..4084ea6 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1078,8 +1078,16 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object, HValue* context = environment()->LookupContext(); - HValue* new_capacity = BuildNewElementsCapacity(context, key); + HValue* max_gap = Add(static_cast(JSObject::kMaxGap)); + HValue* max_capacity = AddInstruction( + HAdd::New(zone, context, current_capacity, max_gap)); + IfBuilder key_checker(this); + key_checker.If(key, max_capacity, Token::LT); + key_checker.Then(); + key_checker.ElseDeopt(); + key_checker.End(); + HValue* new_capacity = BuildNewElementsCapacity(context, key); HValue* new_elements = BuildGrowElementsCapacity(object, elements, kind, kind, length, new_capacity); @@ -1337,6 +1345,9 @@ HValue* HGraphBuilder::BuildAllocateElementsAndInitializeElementsHeader( HValue* context, ElementsKind kind, HValue* capacity) { + // The HForceRepresentation is to prevent possible deopt on int-smi + // conversion after allocation but before the new object fields are set. + capacity = Add(capacity, Representation::Smi()); HValue* new_elements = BuildAllocateElements(context, kind, capacity); BuildInitializeElementsHeader(new_elements, kind, capacity); return new_elements; @@ -1474,7 +1485,6 @@ HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* context, HValue* half_old_capacity = AddInstruction(HShr::New(zone, context, old_capacity, graph_->GetConstant1())); - half_old_capacity->ClearFlag(HValue::kCanOverflow); HValue* new_capacity = AddInstruction( HAdd::New(zone, context, half_old_capacity, old_capacity)); @@ -1497,8 +1507,6 @@ void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) { int max_size = heap->MaxRegularSpaceAllocationSize() / element_size; max_size -= JSArray::kSize / element_size; HConstant* max_size_constant = Add(max_size); - // Since we're forcing Integer32 representation for this HBoundsCheck, - // there's no need to Smi-check the index. Add(length, max_size_constant); } @@ -1927,6 +1935,14 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, bool fill_with_hole) { HValue* context = builder()->environment()->LookupContext(); + // 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. + capacity = builder()->Add(capacity, + Representation::Smi()); + length_field = builder()->Add(length_field, + Representation::Smi()); + // Allocate (dealing with failure appropriately) HAllocate::Flags flags = HAllocate::DefaultFlags(kind_); HAllocate* new_object = builder()->Add(context, size_in_bytes, diff --git a/test/mjsunit/array-store-and-grow.js b/test/mjsunit/array-store-and-grow.js index 88f3db8..dca6749 100644 --- a/test/mjsunit/array-store-and-grow.js +++ b/test/mjsunit/array-store-and-grow.js @@ -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: --allow-natives-syntax + // Verifies that the KeyedStoreIC correctly handles out-of-bounds stores // to an array that grow it by a single element. Test functions are // called twice to make sure that the IC is used, first call is handled @@ -184,3 +186,23 @@ a = []; array_store_1(a, 0, 0.5); assertEquals(0.5, a[0]); assertEquals(0.5, array_store_1([], 0, 0.5)); + +// Verify that a grow store will deoptimize if the max gap (difference between +// the end of an array capacity and a new index) is passed. The wrapper is to +// make sure array_store_10 isn't inlined. + +(function() { + function grow_store(a,b,c) { + a[b] = c; + } + + a = new Array(1); + grow_store(a,1,1); + grow_store(a,2,1); + %OptimizeFunctionOnNextCall(grow_store); + grow_store(a,10,1); + assertOptimized(grow_store); + grow_store(a,2048,1); + assertUnoptimized(grow_store); +})(); + -- 2.7.4