Version 2.17.0-87.0.dev
Merge commit '72eb59f32af1bc9e74c1e3fe1190214dbf4aaef0' into 'dev'
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 0e7a462..c22fcc0 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -84,7 +84,7 @@
}
template <typename T>
- static inline T RoundDown(T x, intptr_t n) {
+ static constexpr inline T RoundDown(T x, intptr_t n) {
ASSERT(IsPowerOfTwo(n));
return (x & -n);
}
@@ -95,7 +95,7 @@
}
template <typename T>
- static inline T RoundUp(T x, intptr_t n) {
+ static constexpr inline T RoundUp(T x, intptr_t n) {
return RoundDown(x + n - 1, n);
}
diff --git a/runtime/vm/compiler/write_barrier_elimination.cc b/runtime/vm/compiler/write_barrier_elimination.cc
index 855cffb..86f58bd 100644
--- a/runtime/vm/compiler/write_barrier_elimination.cc
+++ b/runtime/vm/compiler/write_barrier_elimination.cc
@@ -114,7 +114,7 @@
// Bitvector with all non-Array-allocation instructions set. Used to
// un-mark Array allocations as usable.
- BitVector* array_allocations_mask_;
+ BitVector* large_array_allocations_mask_;
// Bitvectors for each block of which allocations are new or remembered
// at the start (after Phis).
@@ -189,8 +189,21 @@
}
}
+static bool IsCreateLargeArray(Definition* defn) {
+ if (auto create_array = defn->AsCreateArray()) {
+ static_assert(!Array::UseCardMarkingForAllocation(
+ Array::kMaxLengthForWriteBarrierElimination),
+ "Invariant restoration code does not handle card marking.");
+ // Note: IsUsable would reject CreateArray instructions with non-constant
+ // number of elements.
+ return create_array->GetConstantNumElements() >
+ Array::kMaxLengthForWriteBarrierElimination;
+ }
+ return false;
+}
+
void WriteBarrierElimination::IndexDefinitions(Zone* zone) {
- BitmapBuilder array_allocations;
+ BitmapBuilder large_array_allocations;
GrowableArray<Definition*> create_array_worklist;
@@ -198,7 +211,7 @@
BlockEntryInstr* const block = block_order_->At(i);
if (auto join_block = block->AsJoinEntry()) {
for (PhiIterator it(join_block); !it.Done(); it.Advance()) {
- array_allocations.Set(definition_count_, false);
+ large_array_allocations.Set(definition_count_, false);
definition_indices_.Insert({it.Current(), definition_count_++});
#if defined(DEBUG)
if (tracing_) {
@@ -211,10 +224,10 @@
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
if (Definition* current = it.Current()->AsDefinition()) {
if (IsUsable(current)) {
- const bool is_create_array = current->IsCreateArray();
- array_allocations.Set(definition_count_, is_create_array);
+ const bool is_create_large_array = IsCreateLargeArray(current);
+ large_array_allocations.Set(definition_count_, is_create_large_array);
definition_indices_.Insert({current, definition_count_++});
- if (is_create_array) {
+ if (is_create_large_array) {
create_array_worklist.Add(current);
}
#if defined(DEBUG)
@@ -234,8 +247,9 @@
it.Advance()) {
if (auto phi_use = it.Current()->instruction()->AsPhi()) {
const intptr_t index = Index(phi_use);
- if (!array_allocations.Get(index)) {
- array_allocations.Set(index, /*can_be_create_array=*/true);
+ if (!large_array_allocations.Get(index)) {
+ large_array_allocations.Set(index,
+ /*can_be_create_large_array=*/true);
create_array_worklist.Add(phi_use);
}
}
@@ -244,9 +258,9 @@
vector_ = new (zone) BitVector(zone, definition_count_);
vector_->SetAll();
- array_allocations_mask_ = new (zone) BitVector(zone, definition_count_);
+ large_array_allocations_mask_ = new (zone) BitVector(zone, definition_count_);
for (intptr_t i = 0; i < definition_count_; ++i) {
- if (!array_allocations.Get(i)) array_allocations_mask_->Add(i);
+ if (!large_array_allocations.Get(i)) large_array_allocations_mask_->Add(i);
}
}
@@ -388,9 +402,9 @@
if (current->CanCallDart()) {
vector_->Clear();
} else if (current->CanTriggerGC()) {
- // Clear array allocations. These are not added to the remembered set
- // by Thread::RememberLiveTemporaries() after a scavenge.
- vector_->Intersect(array_allocations_mask_);
+ // Clear large array allocations. These are not added to the remembered
+ // set by Thread::RememberLiveTemporaries() after a scavenge.
+ vector_->Intersect(large_array_allocations_mask_);
}
if (AllocationInstr* const alloc = current->AsAllocation()) {
diff --git a/runtime/vm/compiler/write_barrier_elimination_test.cc b/runtime/vm/compiler/write_barrier_elimination_test.cc
index 1275b14..284d112 100644
--- a/runtime/vm/compiler/write_barrier_elimination_test.cc
+++ b/runtime/vm/compiler/write_barrier_elimination_test.cc
@@ -138,14 +138,14 @@
EXPECT(store->ShouldEmitStoreBarrier() == true);
}
-ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_Arrays) {
+static void TestWBEForArrays(int length) {
DEBUG_ONLY(
SetFlagScope<bool> sfs(&FLAG_trace_write_barrier_elimination, true));
const char* nullable_tag = TestCase::NullableTag();
- // Test that array allocations are not considered usable after a
- // may-trigger-GC instruction (in this case CheckStackOverflow), unlike
- // normal allocations, which are only interruped by a Dart call.
+ // Test that array allocations are considered usable after a
+ // may-trigger-GC instruction (in this case CheckStackOverflow) iff they
+ // are small.
// clang-format off
auto kScript =
Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
@@ -159,7 +159,8 @@
foo(int x) {
C c = C();
C n = C();
- List<C%s> array = List<C%s>.filled(1, null);
+ List<C%s> array = List<C%s>.filled(%d, null);
+ array[0] = c;
while (x --> 0) {
c.next = n;
n = c;
@@ -170,10 +171,15 @@
}
main() { foo(10); }
- )", TestCase::LateTag(), nullable_tag, nullable_tag), std::free);
+ )", TestCase::LateTag(), nullable_tag, nullable_tag, length), std::free);
// clang-format on
- const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
+ // Generate a length dependent test library uri.
+ char lib_uri[256];
+ snprintf(lib_uri, sizeof(lib_uri), "%s%d", RESOLVED_USER_TEST_URI, length);
+
+ const auto& root_library = Library::Handle(
+ LoadTestScript(kScript.get(), /*resolver=*/nullptr, lib_uri));
Invoke(root_library, "main");
@@ -185,11 +191,14 @@
EXPECT(entry != nullptr);
StoreInstanceFieldInstr* store_into_c = nullptr;
- StoreIndexedInstr* store_into_array = nullptr;
+ StoreIndexedInstr* store_into_array_before_loop = nullptr;
+ StoreIndexedInstr* store_into_array_after_loop = nullptr;
ILMatcher cursor(flow_graph, entry);
RELEASE_ASSERT(cursor.TryMatch({
kMoveGlob,
+ {kMatchAndMoveStoreIndexed, &store_into_array_before_loop},
+ kMoveGlob,
kMatchAndMoveGoto,
kMoveGlob,
kMatchAndMoveBranchTrue,
@@ -200,11 +209,19 @@
kMoveGlob,
kMatchAndMoveBranchFalse,
kMoveGlob,
- {kMatchAndMoveStoreIndexed, &store_into_array},
+ {kMatchAndMoveStoreIndexed, &store_into_array_after_loop},
}));
EXPECT(store_into_c->ShouldEmitStoreBarrier() == false);
- EXPECT(store_into_array->ShouldEmitStoreBarrier() == true);
+ EXPECT(store_into_array_before_loop->ShouldEmitStoreBarrier() == false);
+ EXPECT(store_into_array_after_loop->ShouldEmitStoreBarrier() ==
+ (length > Array::kMaxLengthForWriteBarrierElimination));
+}
+
+ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_Arrays) {
+ TestWBEForArrays(1);
+ TestWBEForArrays(Array::kMaxLengthForWriteBarrierElimination);
+ TestWBEForArrays(Array::kMaxLengthForWriteBarrierElimination + 1);
}
ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_Regress43786) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b8d8a8a..8497df0 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -668,7 +668,7 @@
Heap::Space space,
bool compressed);
- static intptr_t RoundedAllocationSize(intptr_t size) {
+ static constexpr intptr_t RoundedAllocationSize(intptr_t size) {
return Utils::RoundUp(size, kObjectAlignment);
}
@@ -10228,10 +10228,17 @@
class Array : public Instance {
public:
// Returns `true` if we use card marking for arrays of length [array_length].
- static bool UseCardMarkingForAllocation(const intptr_t array_length) {
+ static constexpr bool UseCardMarkingForAllocation(
+ const intptr_t array_length) {
return Array::InstanceSize(array_length) > Heap::kNewAllocatableSize;
}
+ // WB invariant restoration code only applies to arrives which have at most
+ // this many elements. Consequently WB elimination code should not eliminate
+ // WB on arrays of larger lengths across instructions that can cause GC.
+ // Note: we also can't restore WB invariant for arrays which use card marking.
+ static constexpr intptr_t kMaxLengthForWriteBarrierElimination = 8;
+
intptr_t Length() const { return LengthOf(ptr()); }
static intptr_t LengthOf(const ArrayPtr array) {
return Smi::Value(array->untag()->length());
@@ -10327,7 +10334,7 @@
return OFFSET_OF(UntaggedArray, type_arguments_);
}
- static bool IsValidLength(intptr_t len) {
+ static constexpr bool IsValidLength(intptr_t len) {
return 0 <= len && len <= kMaxElements;
}
@@ -10337,7 +10344,7 @@
return 0;
}
- static intptr_t InstanceSize(intptr_t len) {
+ static constexpr intptr_t InstanceSize(intptr_t len) {
// Ensure that variable length data is not adding to the object length.
ASSERT(sizeof(UntaggedArray) ==
(sizeof(UntaggedInstance) + (2 * kBytesPerElement)));
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index 584cb9c..e21a66c 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -657,10 +657,15 @@
// Stores into new-space objects don't need a write barrier.
if (obj->IsSmiOrNewObject()) continue;
- // To avoid adding too much work into the remembered set, skip
+ // To avoid adding too much work into the remembered set, skip large
// arrays. Write barrier elimination will not remove the barrier
// if we can trigger GC between array allocation and store.
- if (obj->GetClassId() == kArrayCid) continue;
+ if (obj->GetClassId() == kArrayCid) {
+ const auto length = Smi::Value(Array::RawCast(obj)->untag()->length());
+ if (length > Array::kMaxLengthForWriteBarrierElimination) {
+ continue;
+ }
+ }
// Dart code won't store into VM-internal objects except Contexts and
// UnhandledExceptions. This assumption is checked by an assertion in
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index ee371e7..0e4dcd1 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -70,10 +70,10 @@
Future _awaitHelper(var object, dynamic Function(dynamic) thenCallback,
dynamic Function(dynamic, StackTrace) errorCallback, Function awaiter) {
late _Future future;
- if (object is! Future) {
- future = new _Future().._setValue(object);
- } else if (object is _Future) {
+ if (object is _Future) {
future = object;
+ } else if (object is! Future) {
+ future = new _Future().._setValue(object);
} else {
return object.then(thenCallback, onError: errorCallback);
}
diff --git a/tools/VERSION b/tools/VERSION
index 2811149..ee720e4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 86
+PRERELEASE 87
PRERELEASE_PATCH 0
\ No newline at end of file