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

Unified Diff: runtime/vm/flow_graph_optimizer.cc

Issue 868283002: Fix LoadOptimizer's handling of load/stores with constant indices for TypedData. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months 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
« no previous file with comments | « runtime/vm/bitfield.h ('k') | runtime/vm/locations.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/flow_graph_optimizer.cc
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 44b99ba95a09774d15828b216495e84ea43dd012..d0289c6e63b67dcbdcbfcaa56cece2ee6c6b8797 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -5087,6 +5087,24 @@ void LICM::Optimize() {
// - X[*] - non-constant index inside an allocated object X;
// - X[C] - constant index inside an allocated object X.
//
+// Constant indexed places are divided into two subcategories:
+//
+// - Access to homogeneous array-like objects: Array, ImmutableArray,
+// OneByteString, TwoByteString. These objects can only be accessed
+// on element by element basis with all elements having the same size.
+// This means X[C] aliases X[K] if and only if C === K.
+// - TypedData accesses. TypedData allow to read one of the primitive
+// data types at the given byte offset. When TypedData is accessed through
+// index operator on a typed array or a typed array view it is guaranteed
+// that the byte offset is always aligned by the element size. We write
+// these accesses as X[C|S], where C is constant byte offset and S is size
+// of the data type. Obviously X[C|S] and X[K|U] alias if and only if either
+// C = RoundDown(K, S) or K = RoundDown(C, U).
+// Note that not all accesses to typed data are aligned: e.g. ByteData
+// allows unanaligned access through it's get*/set* methods.
+// Check in Place::SetIndex ensures that we never create a place X[C|S]
+// such that C is not aligned by S.
+//
// Separating allocations from other objects improves precision of the
// load forwarding pass because of the following two properties:
//
@@ -5122,10 +5140,38 @@ class Place : public ValueObject {
kConstantIndexed,
};
+ // Size of the element accessed by constant index. Size is only important
+ // for TypedData because those accesses can alias even when constant indexes
+ // are not the same: X[0|4] aliases X[0|2] and X[2|2].
+ enum ElementSize {
+ // If indexed access is not a TypedData access then element size is not
+ // important because there is only a single possible access size depending
+ // on the receiver - X[C] aliases X[K] if and only if C == K.
+ // This is the size set for Array, ImmutableArray, OneByteString and
+ // TwoByteString accesses.
+ kNoSize,
+
+ // 1 byte (Int8List, Uint8List, Uint8ClampedList).
+ kInt8,
+
+ // 2 bytes (Int16List, Uint16List).
+ kInt16,
+
+ // 4 bytes (Int32List, Uint32List, Float32List).
+ kInt32,
+
+ // 8 bytes (Int64List, Uint64List, Float64List).
+ kInt64,
+
+ // 16 bytes (Int32x4List, Float32x4List, Float64x2List).
+ kInt128,
+
+ kLargestElementSize = kInt128,
+ };
+
Place(const Place& other)
: ValueObject(),
- kind_(other.kind_),
- representation_(other.representation_),
+ flags_(other.flags_),
instance_(other.instance_),
raw_selector_(other.raw_selector_),
id_(other.id_) {
@@ -5134,21 +5180,20 @@ class Place : public ValueObject {
// Construct a place from instruction if instruction accesses any place.
// Otherwise constructs kNone place.
Place(Instruction* instr, bool* is_load, bool* is_store)
- : kind_(kNone),
- representation_(kNoRepresentation),
+ : flags_(0),
instance_(NULL),
raw_selector_(0),
id_(0) {
switch (instr->tag()) {
case Instruction::kLoadField: {
LoadFieldInstr* load_field = instr->AsLoadField();
- representation_ = load_field->representation();
+ set_representation(load_field->representation());
instance_ = load_field->instance()->definition()->OriginalDefinition();
if (load_field->field() != NULL) {
- kind_ = kField;
+ set_kind(kField);
field_ = load_field->field();
} else {
- kind_ = kVMField;
+ set_kind(kVMField);
offset_in_bytes_ = load_field->offset_in_bytes();
}
*is_load = true;
@@ -5158,14 +5203,14 @@ class Place : public ValueObject {
case Instruction::kStoreInstanceField: {
StoreInstanceFieldInstr* store =
instr->AsStoreInstanceField();
- representation_ = store->RequiredInputRepresentation(
- StoreInstanceFieldInstr::kValuePos);
+ set_representation(store->RequiredInputRepresentation(
+ StoreInstanceFieldInstr::kValuePos));
instance_ = store->instance()->definition()->OriginalDefinition();
if (!store->field().IsNull()) {
- kind_ = kField;
+ set_kind(kField);
field_ = &store->field();
} else {
- kind_ = kVMField;
+ set_kind(kVMField);
offset_in_bytes_ = store->offset_in_bytes();
}
*is_store = true;
@@ -5173,35 +5218,39 @@ class Place : public ValueObject {
}
case Instruction::kLoadStaticField:
- kind_ = kField;
- representation_ = instr->AsLoadStaticField()->representation();
+ set_kind(kField);
+ set_representation(instr->AsLoadStaticField()->representation());
field_ = &instr->AsLoadStaticField()->StaticField();
*is_load = true;
break;
case Instruction::kStoreStaticField:
- kind_ = kField;
- representation_ = instr->AsStoreStaticField()->
- RequiredInputRepresentation(StoreStaticFieldInstr::kValuePos);
+ set_kind(kField);
+ set_representation(instr->AsStoreStaticField()->
+ RequiredInputRepresentation(StoreStaticFieldInstr::kValuePos));
field_ = &instr->AsStoreStaticField()->field();
*is_store = true;
break;
case Instruction::kLoadIndexed: {
LoadIndexedInstr* load_indexed = instr->AsLoadIndexed();
- representation_ = load_indexed->representation();
+ set_representation(load_indexed->representation());
instance_ = load_indexed->array()->definition()->OriginalDefinition();
- SetIndex(load_indexed->index()->definition());
+ SetIndex(load_indexed->index()->definition(),
+ load_indexed->index_scale(),
+ load_indexed->class_id());
*is_load = true;
break;
}
case Instruction::kStoreIndexed: {
StoreIndexedInstr* store_indexed = instr->AsStoreIndexed();
- representation_ = store_indexed->
- RequiredInputRepresentation(StoreIndexedInstr::kValuePos);
+ set_representation(store_indexed->
+ RequiredInputRepresentation(StoreIndexedInstr::kValuePos));
instance_ = store_indexed->array()->definition()->OriginalDefinition();
- SetIndex(store_indexed->index()->definition());
+ SetIndex(store_indexed->index()->definition(),
+ store_indexed->index_scale(),
+ store_indexed->class_id());
*is_store = true;
break;
}
@@ -5214,7 +5263,10 @@ class Place : public ValueObject {
// Create object representing *[*] alias.
static Place* CreateAnyInstanceAnyIndexAlias(Isolate* isolate,
intptr_t id) {
- return Wrap(isolate, Place(kIndexed, NULL, 0), id);
+ return Wrap(isolate, Place(
+ EncodeFlags(kIndexed, kNoRepresentation, kNoSize),
+ NULL,
+ 0), id);
}
// Return least generic alias for this place. Given that aliases are
@@ -5229,10 +5281,12 @@ class Place : public ValueObject {
// respectively;
// - for non-constant indexed places X[i] we drop information about the
// index obtaining alias X[*].
+ // - we drop information about representation, but keep element size
+ // if any.
//
Place ToAlias() const {
return Place(
- kind_,
+ RepresentationBits::update(kNoRepresentation, flags_),
(DependsOnInstance() && IsAllocation(instance())) ? instance() : NULL,
(kind() == kIndexed) ? 0 : raw_selector_);
}
@@ -5257,20 +5311,41 @@ class Place : public ValueObject {
// wild-card dependent alias *.f, *.@offs, *[C] or *[*] respectively.
Place CopyWithoutInstance() const {
ASSERT(DependsOnInstance());
- return Place(kind_, NULL, raw_selector_);
+ return Place(flags_, NULL, raw_selector_);
}
// Given alias X[C] or *[C] return X[*] and *[*] respectively.
Place CopyWithoutIndex() const {
- ASSERT(kind_ == kConstantIndexed);
- return Place(kIndexed, instance_, 0);
+ ASSERT(kind() == kConstantIndexed);
+ return Place(EncodeFlags(kIndexed, kNoRepresentation, kNoSize),
+ instance_,
+ 0);
}
+ // Given alias X[ByteOffs|S] and a larger element size S', return
+ // alias X[RoundDown(ByteOffs, S')|S'] - this is the byte offset of a larger
+ // typed array element that contains this typed array element.
+ // In other words this method computes the only possible place with the given
+ // size that can alias this place (due to alignment restrictions).
+ // For example for X[9|kInt8] and target size kInt32 we would return
+ // X[8|kInt32].
+ Place ToLargerElement(ElementSize to) const {
+ ASSERT(kind() == kConstantIndexed);
+ ASSERT(element_size() != kNoSize);
+ ASSERT(element_size() < to);
+ return Place(ElementSizeBits::update(to, flags_),
+ instance_,
+ RoundByteOffset(to, index_constant_));
+ }
+
+
intptr_t id() const { return id_; }
- Kind kind() const { return kind_; }
+ Kind kind() const { return KindBits::decode(flags_); }
- Representation representation() const { return representation_; }
+ Representation representation() const {
+ return RepresentationBits::decode(flags_);
+ }
Definition* instance() const {
ASSERT(DependsOnInstance());
@@ -5283,22 +5358,26 @@ class Place : public ValueObject {
}
const Field& field() const {
- ASSERT(kind_ == kField);
+ ASSERT(kind() == kField);
return *field_;
}
intptr_t offset_in_bytes() const {
- ASSERT(kind_ == kVMField);
+ ASSERT(kind() == kVMField);
return offset_in_bytes_;
}
Definition* index() const {
- ASSERT(kind_ == kIndexed);
+ ASSERT(kind() == kIndexed);
return index_;
}
+ ElementSize element_size() const {
+ return ElementSizeBits::decode(flags_);
+ }
+
intptr_t index_constant() const {
- ASSERT(kind_ == kConstantIndexed);
+ ASSERT(kind() == kConstantIndexed);
return index_constant_;
}
@@ -5312,7 +5391,7 @@ class Place : public ValueObject {
}
const char* ToCString() const {
- switch (kind_) {
+ switch (kind()) {
case kNone:
return "<none>";
@@ -5340,10 +5419,18 @@ class Place : public ValueObject {
DefinitionName(index()));
case kConstantIndexed:
- return Isolate::Current()->current_zone()->PrintToString(
- "<%s[%" Pd "]>",
- DefinitionName(instance()),
- index_constant());
+ if (element_size() == kNoSize) {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "<%s[%" Pd "]>",
+ DefinitionName(instance()),
+ index_constant());
+ } else {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "<%s[%" Pd "|%" Pd "]>",
+ DefinitionName(instance()),
+ index_constant(),
+ ElementSizeMultiplier(element_size()));
+ }
}
UNREACHABLE();
return "<?>";
@@ -5354,13 +5441,12 @@ class Place : public ValueObject {
}
intptr_t Hashcode() const {
- return (kind_ * 63 + reinterpret_cast<intptr_t>(instance_)) * 31 +
- representation_ * 15 + FieldHashcode();
+ return (flags_ * 63 + reinterpret_cast<intptr_t>(instance_)) * 31 +
+ FieldHashcode();
}
bool Equals(const Place* other) const {
- return (kind_ == other->kind_) &&
- (representation_ == other->representation_) &&
+ return (flags_ == other->flags_) &&
(instance_ == other->instance_) &&
SameField(other);
}
@@ -5378,37 +5464,132 @@ class Place : public ValueObject {
}
private:
- Place(Kind kind, Definition* instance, intptr_t selector)
- : kind_(kind),
- representation_(kNoRepresentation),
+ Place(uword flags, Definition* instance, intptr_t selector)
+ : flags_(flags),
instance_(instance),
raw_selector_(selector),
id_(0) {
}
bool SameField(const Place* other) const {
- return (kind_ == kField) ? (field().raw() == other->field().raw())
- : (offset_in_bytes_ == other->offset_in_bytes_);
+ return (kind() == kField) ? (field().raw() == other->field().raw())
+ : (offset_in_bytes_ == other->offset_in_bytes_);
}
intptr_t FieldHashcode() const {
- return (kind_ == kField) ? reinterpret_cast<intptr_t>(field().raw())
- : offset_in_bytes_;
+ return (kind() == kField) ? reinterpret_cast<intptr_t>(field().raw())
+ : offset_in_bytes_;
+ }
+
+ void set_representation(Representation rep) {
+ flags_ = RepresentationBits::update(rep, flags_);
+ }
+
+ void set_kind(Kind kind) {
+ flags_ = KindBits::update(kind, flags_);
+ }
+
+ void set_element_size(ElementSize scale) {
+ flags_ = ElementSizeBits::update(scale, flags_);
}
- void SetIndex(Definition* index) {
+ void SetIndex(Definition* index, intptr_t scale, intptr_t class_id) {
ConstantInstr* index_constant = index->AsConstant();
if ((index_constant != NULL) && index_constant->value().IsSmi()) {
- kind_ = kConstantIndexed;
- index_constant_ = Smi::Cast(index_constant->value()).Value();
- } else {
- kind_ = kIndexed;
- index_ = index;
+ const intptr_t index_value = Smi::Cast(index_constant->value()).Value();
+ const ElementSize size = ElementSizeFor(class_id);
+ const bool is_typed_data = (size != kNoSize);
+
+ // If we are writing into the typed data scale the index to
+ // get byte offset. Otherwise ignore the scale.
+ if (!is_typed_data) {
+ scale = 1;
+ }
+
+ // Guard against potential multiplication overflow and negative indices.
+ if ((0 <= index_value) && (index_value < (kMaxInt32 / scale))) {
+ const intptr_t scaled_index = index_value * scale;
+
+ // Guard against unaligned byte offsets.
+ if (!is_typed_data ||
+ Utils::IsAligned(scaled_index, ElementSizeMultiplier(size))) {
+ set_kind(kConstantIndexed);
+ set_element_size(size);
+ index_constant_ = scaled_index;
+ return;
+ }
+ }
+
+ // Fallthrough: create generic _[*] place.
+ }
+
+ set_kind(kIndexed);
+ index_ = index;
+ }
+
+ static uword EncodeFlags(Kind kind, Representation rep, ElementSize scale) {
+ ASSERT((kind == kConstantIndexed) || (scale == kNoSize));
+ return KindBits::encode(kind) |
+ RepresentationBits::encode(rep) |
+ ElementSizeBits::encode(scale);
+ }
+
+ static ElementSize ElementSizeFor(intptr_t class_id) {
+ switch (class_id) {
+ case kArrayCid:
+ case kImmutableArrayCid:
+ case kOneByteStringCid:
+ case kTwoByteStringCid:
+ // Object arrays and strings do not allow accessing them through
+ // different types. No need to attach scale.
+ return kNoSize;
+
+ case kTypedDataInt8ArrayCid:
+ case kTypedDataUint8ArrayCid:
+ case kTypedDataUint8ClampedArrayCid:
+ case kExternalTypedDataUint8ArrayCid:
+ case kExternalTypedDataUint8ClampedArrayCid:
+ return kInt8;
+
+ case kTypedDataInt16ArrayCid:
+ case kTypedDataUint16ArrayCid:
+ return kInt16;
+
+ case kTypedDataInt32ArrayCid:
+ case kTypedDataUint32ArrayCid:
+ case kTypedDataFloat32ArrayCid:
+ return kInt32;
+
+ case kTypedDataInt64ArrayCid:
+ case kTypedDataUint64ArrayCid:
+ case kTypedDataFloat64ArrayCid:
+ return kInt64;
+
+ case kTypedDataInt32x4ArrayCid:
+ case kTypedDataFloat32x4ArrayCid:
+ case kTypedDataFloat64x2ArrayCid:
+ return kInt128;
+
+ default:
+ UNREACHABLE();
+ return kNoSize;
}
}
- Kind kind_;
- Representation representation_;
+ static intptr_t ElementSizeMultiplier(ElementSize size) {
+ return 1 << (static_cast<intptr_t>(size) - static_cast<intptr_t>(kInt8));
+ }
+
+ static intptr_t RoundByteOffset(ElementSize size, intptr_t offset) {
+ return offset & ~(ElementSizeMultiplier(size) - 1);
+ }
+
+ typedef BitField<Kind, 0, 3> KindBits;
+ typedef BitField<Representation, KindBits::kNextBit, 11> RepresentationBits;
+ typedef BitField<
+ ElementSize, RepresentationBits::kNextBit, 3> ElementSizeBits;
+
+ uword flags_;
Definition* instance_;
union {
intptr_t raw_selector_;
@@ -5500,6 +5681,7 @@ class AliasedSet : public ZoneAllocated {
phi_moves_(phi_moves),
aliases_(5),
aliases_map_(),
+ typed_data_access_sizes_(),
representatives_(),
killed_(),
aliased_by_effects_(new(isolate) BitVector(isolate, places->length())) {
@@ -5608,6 +5790,13 @@ class AliasedSet : public ZoneAllocated {
EnsureSet(&representatives_, kUnknownInstanceConstantIndexedAlias)->
Add(place->id());
}
+
+ // Collect all element sizes used to access TypedData arrays in
+ // the function. This is used to skip sizes without representatives
+ // when computing kill sets.
+ if (alias->element_size() != Place::kNoSize) {
+ typed_data_access_sizes_.Add(alias->element_size());
+ }
} else if ((alias->kind() == Place::kIndexed) &&
CanBeAliased(place->instance())) {
EnsureSet(&representatives_, kAnyAllocationIndexedAlias)->
@@ -5656,6 +5845,7 @@ class AliasedSet : public ZoneAllocated {
kAnyInstanceAnyIndexAlias + aliases_.length());
InsertAlias(canonical);
}
+ ASSERT(aliases_map_.Lookup(&alias) == canonical);
return canonical;
}
@@ -5725,6 +5915,39 @@ class AliasedSet : public ZoneAllocated {
break;
case Place::kConstantIndexed: // Either X[C] or *[C] alias.
+ if (alias->element_size() != Place::kNoSize) {
+ const bool has_aliased_instance =
+ (alias->instance() != NULL) && CanBeAliased(alias->instance());
+
+ // If this is a TypedData access then X[C|S] aliases larger elements
+ // covering this one X[RoundDown(C, S')|S'] for all S' > S and
+ // all smaller elements being covered by this one X[C'|S'] for
+ // some S' < S and all C' such that C = RoundDown(C', S).
+ // In the loop below it's enough to only propagate aliasing to
+ // larger aliases because propagation is symmetric: smaller aliases
+ // (if there are any) would update kill set for this alias when they
+ // are visited.
+ for (intptr_t i = static_cast<intptr_t>(alias->element_size()) + 1;
+ i <= Place::kLargestElementSize;
+ i++) {
+ // Skip element sizes that a guaranteed to have no representatives.
+ if (!typed_data_access_sizes_.Contains(alias->element_size())) {
+ continue;
+ }
+
+ // X[C|S] aliases with X[RoundDown(C, S')|S'] and likewise
+ // *[C|S] aliases with *[RoundDown(C, S')|S'].
+ const Place larger_alias =
+ alias->ToLargerElement(static_cast<Place::ElementSize>(i));
+ CrossAlias(alias, larger_alias);
+ if (has_aliased_instance) {
+ // If X is an aliased instance then X[C|S] aliases
+ // with *[RoundDown(C, S')|S'].
+ CrossAlias(alias, larger_alias.CopyWithoutInstance());
+ }
+ }
+ }
+
if (alias->instance() == NULL) {
// *[C] aliases with X[C], X[*], *[*].
AddAllRepresentatives(alias, kAnyAllocationIndexedAlias);
@@ -5917,6 +6140,8 @@ class AliasedSet : public ZoneAllocated {
GrowableArray<const Place*> aliases_;
DirectChainedHashMap<PointerKeyValueTrait<const Place> > aliases_map_;
+ SmallSet<Place::ElementSize> typed_data_access_sizes_;
+
// Maps alias id to set of ids of places representing the alias.
// Place represents an alias if this alias is least generic alias for
// the place.
« no previous file with comments | « runtime/vm/bitfield.h ('k') | runtime/vm/locations.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698