| // 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 "vm/compiler/frontend/kernel_binary_flowgraph.h" |
| |
| #include "vm/compiler/backend/il_test_helper.h" |
| #include "vm/object.h" |
| #include "vm/unit_test.h" |
| |
| namespace dart { |
| |
| ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConstFoldStringConcats) { |
| // According to the Dart spec: |
| // "Adjacent strings are implicitly concatenated to form a single string |
| // literal." |
| const char* kScript = R"( |
| test() { |
| var s = 'aaaa' |
| 'bbbb' |
| 'cccc'; |
| return s; |
| } |
| )"; |
| |
| const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
| const auto& function = Function::Handle(GetFunction(root_library, "test")); |
| |
| Invoke(root_library, "test"); |
| |
| TestPipeline pipeline(function, CompilerPass::kJIT); |
| FlowGraph* flow_graph = pipeline.RunPasses({ |
| CompilerPass::kComputeSSA, |
| }); |
| |
| auto entry = flow_graph->graph_entry()->normal_entry(); |
| EXPECT(entry != nullptr); |
| |
| ReturnInstr* ret = nullptr; |
| |
| ILMatcher cursor(flow_graph, entry); |
| // clang-format off |
| RELEASE_ASSERT(cursor.TryMatch({ |
| kMatchAndMoveFunctionEntry, |
| kMatchAndMoveCheckStackOverflow, |
| kMoveDebugStepChecks, |
| {kMatchReturn, &ret}, |
| })); |
| // clang-format on |
| |
| EXPECT(ret->value()->BindsToConstant()); |
| EXPECT(ret->value()->BoundConstant().IsString()); |
| const String& ret_str = String::Cast(ret->value()->BoundConstant()); |
| EXPECT(ret_str.Equals("aaaabbbbcccc")); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_FlattenNestedStringInterp) { |
| // We should collapse nested StringInterpolates: |
| const char* kScript = R"( |
| test(String s) { |
| return '$s' '${'d' 'e'}'; |
| } |
| main() => test('u'); |
| )"; |
| |
| const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
| const auto& function = Function::Handle(GetFunction(root_library, "test")); |
| |
| Invoke(root_library, "main"); |
| |
| TestPipeline pipeline(function, CompilerPass::kJIT); |
| FlowGraph* flow_graph = pipeline.RunPasses({ |
| CompilerPass::kComputeSSA, |
| }); |
| |
| auto entry = flow_graph->graph_entry()->normal_entry(); |
| EXPECT(entry != nullptr); |
| |
| StoreIndexedInstr* store1 = nullptr; |
| StoreIndexedInstr* store2 = nullptr; |
| |
| ILMatcher cursor(flow_graph, entry); |
| // clang-format off |
| RELEASE_ASSERT(cursor.TryMatch({ |
| kMatchAndMoveFunctionEntry, |
| kMatchAndMoveCheckStackOverflow, |
| kMoveDebugStepChecks, |
| kMatchAndMoveCreateArray, |
| {kMatchAndMoveStoreIndexed, &store1}, |
| {kMatchAndMoveStoreIndexed, &store2}, |
| kMatchAndMoveStringInterpolate, |
| kMoveDebugStepChecks, |
| kMatchReturn, |
| })); |
| // clang-format on |
| |
| // StoreIndexed(tmp_array, 0, s) |
| EXPECT(store1->index()->BindsToConstant()); |
| EXPECT(store1->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0); |
| |
| EXPECT(!store1->value()->BindsToConstant()); |
| |
| // StoreIndexed(tmp_array, 1, "de") |
| EXPECT(store2->index()->BindsToConstant()); |
| EXPECT(store2->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1); |
| |
| EXPECT(store2->value()->BindsToConstant()); |
| EXPECT(store2->value()->BoundConstant().IsString()); |
| EXPECT(String::Cast(store2->value()->BoundConstant()).Equals("de")); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_DropEmptyStringInterp) { |
| // We should drop empty strings from StringInterpolates: |
| const char* kScript = R"( |
| test(s) { |
| return '' 'a' '$s' '' 'b' ''; |
| } |
| main() => test('u'); |
| )"; |
| |
| const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
| const auto& function = Function::Handle(GetFunction(root_library, "test")); |
| |
| Invoke(root_library, "main"); |
| |
| TestPipeline pipeline(function, CompilerPass::kJIT); |
| FlowGraph* flow_graph = pipeline.RunPasses({ |
| CompilerPass::kComputeSSA, |
| }); |
| |
| auto entry = flow_graph->graph_entry()->normal_entry(); |
| EXPECT(entry != nullptr); |
| |
| StoreIndexedInstr* store1 = nullptr; |
| StoreIndexedInstr* store2 = nullptr; |
| StoreIndexedInstr* store3 = nullptr; |
| |
| ILMatcher cursor(flow_graph, entry); |
| // clang-format off |
| RELEASE_ASSERT(cursor.TryMatch({ |
| kMatchAndMoveFunctionEntry, |
| kMatchAndMoveCheckStackOverflow, |
| kMoveDebugStepChecks, |
| kMatchAndMoveCreateArray, |
| {kMatchAndMoveStoreIndexed, &store1}, |
| {kMatchAndMoveStoreIndexed, &store2}, |
| {kMatchAndMoveStoreIndexed, &store3}, |
| kMatchAndMoveStringInterpolate, |
| kMoveDebugStepChecks, |
| kMatchReturn, |
| })); |
| // clang-format on |
| |
| // StoreIndexed(tmp_array, 0, "ab") |
| EXPECT(store1->index()->BindsToConstant()); |
| EXPECT(store1->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0); |
| |
| EXPECT(store1->value()->BindsToConstant()); |
| EXPECT(store1->value()->BoundConstant().IsString()); |
| EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("a")); |
| |
| // StoreIndexed(tmp_array, 1, s) |
| EXPECT(store2->index()->BindsToConstant()); |
| EXPECT(store2->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1); |
| |
| EXPECT(!store2->value()->BindsToConstant()); |
| |
| // StoreIndexed(tmp_array, 2, "b") |
| EXPECT(store3->index()->BindsToConstant()); |
| EXPECT(store3->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2); |
| |
| EXPECT(store3->value()->BindsToConstant()); |
| EXPECT(store3->value()->BoundConstant().IsString()); |
| EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("b")); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(StreamingFlowGraphBuilder_ConcatStringLits) { |
| // We should drop empty strings from StringInterpolates: |
| const char* kScript = R"( |
| test(s) { |
| return '' 'a' '' 'b' '$s' '' 'c' '' 'd' ''; |
| } |
| main() => test('u'); |
| )"; |
| |
| const auto& root_library = Library::Handle(LoadTestScript(kScript)); |
| const auto& function = Function::Handle(GetFunction(root_library, "test")); |
| |
| Invoke(root_library, "main"); |
| |
| TestPipeline pipeline(function, CompilerPass::kJIT); |
| FlowGraph* flow_graph = pipeline.RunPasses({ |
| CompilerPass::kComputeSSA, |
| }); |
| |
| auto entry = flow_graph->graph_entry()->normal_entry(); |
| EXPECT(entry != nullptr); |
| |
| StoreIndexedInstr* store1 = nullptr; |
| StoreIndexedInstr* store2 = nullptr; |
| StoreIndexedInstr* store3 = nullptr; |
| |
| ILMatcher cursor(flow_graph, entry); |
| // clang-format off |
| RELEASE_ASSERT(cursor.TryMatch({ |
| kMatchAndMoveFunctionEntry, |
| kMatchAndMoveCheckStackOverflow, |
| kMoveDebugStepChecks, |
| kMatchAndMoveCreateArray, |
| {kMatchAndMoveStoreIndexed, &store1}, |
| {kMatchAndMoveStoreIndexed, &store2}, |
| {kMatchAndMoveStoreIndexed, &store3}, |
| kMatchAndMoveStringInterpolate, |
| kMoveDebugStepChecks, |
| kMatchReturn, |
| })); |
| // clang-format on |
| |
| // StoreIndexed(tmp_array, 0, "ab") |
| EXPECT(store1->index()->BindsToConstant()); |
| EXPECT(store1->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store1->index()->BoundConstant()).AsInt64Value() == 0); |
| |
| EXPECT(store1->value()->BindsToConstant()); |
| EXPECT(store1->value()->BoundConstant().IsString()); |
| EXPECT(String::Cast(store1->value()->BoundConstant()).Equals("ab")); |
| |
| // StoreIndexed(tmp_array, 1, s) |
| EXPECT(store2->index()->BindsToConstant()); |
| EXPECT(store2->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store2->index()->BoundConstant()).AsInt64Value() == 1); |
| |
| EXPECT(!store2->value()->BindsToConstant()); |
| |
| // StoreIndexed(tmp_array, 2, "cd") |
| EXPECT(store3->index()->BindsToConstant()); |
| EXPECT(store3->index()->BoundConstant().IsInteger()); |
| EXPECT(Integer::Cast(store3->index()->BoundConstant()).AsInt64Value() == 2); |
| |
| EXPECT(store3->value()->BindsToConstant()); |
| EXPECT(store3->value()->BoundConstant().IsString()); |
| EXPECT(String::Cast(store3->value()->BoundConstant()).Equals("cd")); |
| } |
| |
| } // namespace dart |