3a6cf0e6d4
- Some tests were using a 'main' with a signature that does not match the new spec,changed these tests to invoke 'testMain' instead of 'main' - deleted test case for a factory method that returns a null (reports as a compilation error now) - Fixed some of the isolate_reload tests to not crash - Turn on non-nullable experiment flag by default for unit tests - Added a unit test for creation of a non nullable list of strings using the Dart C API. Change-Id: I2c345aaabdc29e9222c1d451f2ef43ba5cdb3686 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167821 Commit-Queue: Siva Annamalai <asiva@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
256 lines
7.3 KiB
C++
256 lines
7.3 KiB
C++
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/backend/il_test_helper.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
namespace dart {
|
|
|
|
DEBUG_ONLY(DECLARE_FLAG(bool, trace_write_barrier_elimination);)
|
|
|
|
ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_JoinSuccessors) {
|
|
DEBUG_ONLY(
|
|
SetFlagScope<bool> sfs(&FLAG_trace_write_barrier_elimination, true));
|
|
const char* nullable_tag = TestCase::NullableTag();
|
|
const char* null_assert_tag = TestCase::NullAssertTag();
|
|
|
|
// This is a regression test for a bug where we were using
|
|
// JoinEntry::SuccessorCount() to determine the number of outgoing blocks
|
|
// from the join block. JoinEntry::SuccessorCount() is in fact always 0;
|
|
// JoinEntry::last_instruction()->SuccessorCount() should be used instead.
|
|
// clang-format off
|
|
auto kScript = Utils::CStringUniquePtr(
|
|
OS::SCreate(nullptr, R"(
|
|
class C {
|
|
int%s value;
|
|
C%s next;
|
|
C%s prev;
|
|
}
|
|
|
|
@pragma("vm:never-inline")
|
|
fn() {}
|
|
|
|
foo(int x) {
|
|
C%s prev = C();
|
|
C%s next;
|
|
while (x --> 0) {
|
|
next = C();
|
|
next%s.prev = prev;
|
|
prev?.next = next;
|
|
prev = next;
|
|
fn();
|
|
}
|
|
return next;
|
|
}
|
|
|
|
main() { foo(10); }
|
|
)",
|
|
nullable_tag, nullable_tag, nullable_tag, nullable_tag,
|
|
nullable_tag, null_assert_tag), std::free);
|
|
// clang-format on
|
|
|
|
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
|
|
TestPipeline pipeline(function, CompilerPass::kJIT);
|
|
FlowGraph* flow_graph = pipeline.RunPasses({});
|
|
|
|
auto entry = flow_graph->graph_entry()->normal_entry();
|
|
EXPECT(entry != nullptr);
|
|
|
|
StoreInstanceFieldInstr* store1 = nullptr;
|
|
StoreInstanceFieldInstr* store2 = nullptr;
|
|
|
|
ILMatcher cursor(flow_graph, entry);
|
|
RELEASE_ASSERT(cursor.TryMatch({
|
|
kMoveGlob,
|
|
kMatchAndMoveGoto,
|
|
kMoveGlob,
|
|
kMatchAndMoveBranchTrue,
|
|
kMoveGlob,
|
|
{kMatchAndMoveStoreInstanceField, &store1},
|
|
kMoveGlob,
|
|
{kMatchAndMoveStoreInstanceField, &store2},
|
|
}));
|
|
|
|
EXPECT(store1->ShouldEmitStoreBarrier() == false);
|
|
EXPECT(store2->ShouldEmitStoreBarrier() == true);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_AtLeastOnce) {
|
|
DEBUG_ONLY(
|
|
SetFlagScope<bool> sfs(&FLAG_trace_write_barrier_elimination, true));
|
|
// Ensure that we process every block at least once during the analysis
|
|
// phase so that the out-sets will be initialized. If we don't process
|
|
// each block at least once, the store "c.next = n" will be marked
|
|
// NoWriteBarrier.
|
|
// clang-format off
|
|
auto kScript = Utils::CStringUniquePtr(OS::SCreate(nullptr,
|
|
R"(
|
|
class C {
|
|
%s C next;
|
|
}
|
|
|
|
@pragma("vm:never-inline")
|
|
fn() {}
|
|
|
|
foo(int x) {
|
|
C c = C();
|
|
C n = C();
|
|
if (x > 5) {
|
|
fn();
|
|
}
|
|
c.next = n;
|
|
return c;
|
|
}
|
|
|
|
main() { foo(0); foo(10); }
|
|
)", TestCase::LateTag()), std::free);
|
|
// clang-format on
|
|
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
|
|
TestPipeline pipeline(function, CompilerPass::kJIT);
|
|
FlowGraph* flow_graph = pipeline.RunPasses({});
|
|
|
|
auto entry = flow_graph->graph_entry()->normal_entry();
|
|
EXPECT(entry != nullptr);
|
|
|
|
StoreInstanceFieldInstr* store = nullptr;
|
|
|
|
ILMatcher cursor(flow_graph, entry);
|
|
RELEASE_ASSERT(cursor.TryMatch({
|
|
kMoveGlob,
|
|
kMatchAndMoveBranchFalse,
|
|
kMoveGlob,
|
|
kMatchAndMoveGoto,
|
|
kMoveGlob,
|
|
{kMatchAndMoveStoreInstanceField, &store},
|
|
}));
|
|
|
|
EXPECT(store->ShouldEmitStoreBarrier() == true);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_Arrays) {
|
|
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.
|
|
// clang-format off
|
|
auto kScript =
|
|
Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
|
|
class C {
|
|
%s C next;
|
|
}
|
|
|
|
@pragma("vm:never-inline")
|
|
fn() {}
|
|
|
|
foo(int x) {
|
|
C c = C();
|
|
C n = C();
|
|
List<C%s> array = List<C%s>.filled(1, null);
|
|
while (x --> 0) {
|
|
c.next = n;
|
|
n = c;
|
|
c = C();
|
|
}
|
|
array[0] = c;
|
|
return array;
|
|
}
|
|
|
|
main() { foo(10); }
|
|
)", TestCase::LateTag(), nullable_tag, nullable_tag), std::free);
|
|
// clang-format on
|
|
|
|
const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
|
|
TestPipeline pipeline(function, CompilerPass::kJIT);
|
|
FlowGraph* flow_graph = pipeline.RunPasses({});
|
|
|
|
auto entry = flow_graph->graph_entry()->normal_entry();
|
|
EXPECT(entry != nullptr);
|
|
|
|
StoreInstanceFieldInstr* store_into_c = nullptr;
|
|
StoreIndexedInstr* store_into_array = nullptr;
|
|
|
|
ILMatcher cursor(flow_graph, entry);
|
|
RELEASE_ASSERT(cursor.TryMatch({
|
|
kMoveGlob,
|
|
kMatchAndMoveGoto,
|
|
kMoveGlob,
|
|
kMatchAndMoveBranchTrue,
|
|
kMoveGlob,
|
|
{kMatchAndMoveStoreInstanceField, &store_into_c},
|
|
kMoveGlob,
|
|
kMatchAndMoveGoto,
|
|
kMoveGlob,
|
|
kMatchAndMoveBranchFalse,
|
|
kMoveGlob,
|
|
{kMatchAndMoveStoreIndexed, &store_into_array},
|
|
}));
|
|
|
|
EXPECT(store_into_c->ShouldEmitStoreBarrier() == false);
|
|
EXPECT(store_into_array->ShouldEmitStoreBarrier() == true);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(IRTest_WriteBarrierElimination_Regress43786) {
|
|
DEBUG_ONLY(
|
|
SetFlagScope<bool> sfs(&FLAG_trace_write_barrier_elimination, true));
|
|
const char* kScript = R"(
|
|
foo() {
|
|
final root = List<dynamic>.filled(128, null);
|
|
List<dynamic> last = root;
|
|
for (int i = 0; i < 10 * 1024; ++i) {
|
|
final nc = List<dynamic>.filled(128, null);
|
|
last[0] = nc;
|
|
last = nc;
|
|
}
|
|
}
|
|
|
|
main() { foo(); }
|
|
)";
|
|
|
|
const auto& root_library = Library::Handle(LoadTestScript(kScript));
|
|
|
|
Invoke(root_library, "main");
|
|
|
|
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
|
|
TestPipeline pipeline(function, CompilerPass::kJIT);
|
|
FlowGraph* flow_graph = pipeline.RunPasses({});
|
|
|
|
auto entry = flow_graph->graph_entry()->normal_entry();
|
|
EXPECT(entry != nullptr);
|
|
|
|
StoreIndexedInstr* store_into_phi = nullptr;
|
|
|
|
ILMatcher cursor(flow_graph, entry);
|
|
RELEASE_ASSERT(cursor.TryMatch(
|
|
{
|
|
kMatchAndMoveCreateArray,
|
|
kMatchAndMoveGoto,
|
|
kMatchAndMoveBranchTrue,
|
|
kMatchAndMoveCreateArray,
|
|
{kMatchAndMoveStoreIndexed, &store_into_phi},
|
|
},
|
|
kMoveGlob));
|
|
|
|
EXPECT(store_into_phi->array()->definition()->IsPhi());
|
|
EXPECT(store_into_phi->ShouldEmitStoreBarrier());
|
|
}
|
|
|
|
} // namespace dart
|