blob: 80ddd91bec28761eeffe1cb3688df44a0e0038ac [file] [log] [blame]
// 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