1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/compiler/backend/il.h"
6
7#include <vector>
8
9#include "platform/utils.h"
10#include "vm/compiler/backend/il_test_helper.h"
11#include "vm/unit_test.h"
12
13namespace dart {
14
15ISOLATE_UNIT_TEST_CASE(InstructionTests) {
16 TargetEntryInstr* target_instr =
17 new TargetEntryInstr(1, kInvalidTryIndex, DeoptId::kNone);
18 EXPECT(target_instr->IsBlockEntry());
19 EXPECT(!target_instr->IsDefinition());
20 SpecialParameterInstr* context = new SpecialParameterInstr(
21 SpecialParameterInstr::kContext, DeoptId::kNone, target_instr);
22 EXPECT(context->IsDefinition());
23 EXPECT(!context->IsBlockEntry());
24 EXPECT(context->GetBlock() == target_instr);
25}
26
27ISOLATE_UNIT_TEST_CASE(OptimizationTests) {
28 JoinEntryInstr* join =
29 new JoinEntryInstr(1, kInvalidTryIndex, DeoptId::kNone);
30
31 Definition* def1 = new PhiInstr(join, 0);
32 Definition* def2 = new PhiInstr(join, 0);
33 Value* use1a = new Value(def1);
34 Value* use1b = new Value(def1);
35 EXPECT(use1a->Equals(use1b));
36 Value* use2 = new Value(def2);
37 EXPECT(!use2->Equals(use1a));
38
39 ConstantInstr* c1 = new ConstantInstr(Bool::True());
40 ConstantInstr* c2 = new ConstantInstr(Bool::True());
41 EXPECT(c1->Equals(c2));
42 ConstantInstr* c3 = new ConstantInstr(Object::ZoneHandle());
43 ConstantInstr* c4 = new ConstantInstr(Object::ZoneHandle());
44 EXPECT(c3->Equals(c4));
45 EXPECT(!c3->Equals(c1));
46}
47
48ISOLATE_UNIT_TEST_CASE(IRTest_EliminateWriteBarrier) {
49 const char* nullable_tag = TestCase::NullableTag();
50 // clang-format off
51 auto kScript = Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
52 class Container<T> {
53 operator []=(var index, var value) {
54 return data[index] = value;
55 }
56
57 List<T%s> data = List<T%s>.filled(10, null);
58 }
59
60 Container<int> x = Container<int>();
61
62 foo() {
63 for (int i = 0; i < 10; ++i) {
64 x[i] = i;
65 }
66 }
67 )", nullable_tag, nullable_tag), std::free);
68 // clang-format on
69
70 const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
71 const auto& function = Function::Handle(GetFunction(root_library, "foo"));
72
73 Invoke(root_library, "foo");
74
75 TestPipeline pipeline(function, CompilerPass::kJIT);
76 FlowGraph* flow_graph = pipeline.RunPasses({});
77
78 auto entry = flow_graph->graph_entry()->normal_entry();
79 EXPECT(entry != nullptr);
80
81 StoreIndexedInstr* store_indexed = nullptr;
82
83 ILMatcher cursor(flow_graph, entry, true);
84 RELEASE_ASSERT(cursor.TryMatch({
85 kMoveGlob,
86 kMatchAndMoveBranchTrue,
87 kMoveGlob,
88 {kMatchStoreIndexed, &store_indexed},
89 }));
90
91 EXPECT(!store_indexed->value()->NeedsWriteBarrier());
92}
93
94static void ExpectStores(FlowGraph* flow_graph,
95 const std::vector<const char*>& expected_stores) {
96 size_t next_expected_store = 0;
97 for (BlockIterator block_it = flow_graph->reverse_postorder_iterator();
98 !block_it.Done(); block_it.Advance()) {
99 for (ForwardInstructionIterator it(block_it.Current()); !it.Done();
100 it.Advance()) {
101 if (auto store = it.Current()->AsStoreInstanceField()) {
102 EXPECT_LT(next_expected_store, expected_stores.size());
103 EXPECT_STREQ(expected_stores[next_expected_store],
104 store->slot().Name());
105 next_expected_store++;
106 }
107 }
108 }
109}
110
111static void RunInitializingStoresTest(
112 const Library& root_library,
113 const char* function_name,
114 CompilerPass::PipelineMode mode,
115 const std::vector<const char*>& expected_stores) {
116 const auto& function =
117 Function::Handle(GetFunction(root_library, function_name));
118 TestPipeline pipeline(function, mode);
119 FlowGraph* flow_graph = pipeline.RunPasses({
120 CompilerPass::kComputeSSA,
121 CompilerPass::kTypePropagation,
122 CompilerPass::kApplyICData,
123 CompilerPass::kInlining,
124 CompilerPass::kTypePropagation,
125 CompilerPass::kSelectRepresentations,
126 CompilerPass::kCanonicalize,
127 CompilerPass::kConstantPropagation,
128 });
129 ASSERT(flow_graph != nullptr);
130 ExpectStores(flow_graph, expected_stores);
131}
132
133ISOLATE_UNIT_TEST_CASE(IRTest_InitializingStores) {
134 // clang-format off
135 auto kScript = Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
136 class Bar {
137 var f;
138 var g;
139
140 Bar({this.f, this.g});
141 }
142 Bar f1() => Bar(f: 10);
143 Bar f2() => Bar(g: 10);
144 f3() {
145 return () { };
146 }
147 f4<T>({T%s value}) {
148 return () { return value; };
149 }
150 main() {
151 f1();
152 f2();
153 f3();
154 f4();
155 }
156 )",
157 TestCase::NullableTag()), std::free);
158 // clang-format on
159
160 const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
161 Invoke(root_library, "main");
162
163 RunInitializingStoresTest(root_library, "f1", CompilerPass::kJIT,
164 /*expected_stores=*/{"f"});
165 RunInitializingStoresTest(root_library, "f2", CompilerPass::kJIT,
166 /*expected_stores=*/{"g"});
167 RunInitializingStoresTest(root_library, "f3", CompilerPass::kJIT,
168 /*expected_stores=*/
169 {"Closure.function"});
170
171 // Note that in JIT mode we lower context allocation in a way that hinders
172 // removal of initializing moves so there would be some redundant stores of
173 // null left in the graph. In AOT mode we don't apply this optimization
174 // which enables us to remove more stores.
175 std::vector<const char*> expected_stores_jit;
176 std::vector<const char*> expected_stores_aot;
177 if (root_library.is_declared_in_bytecode()) {
178 // Bytecode flow graph builder doesn't provide readable
179 // variable names for captured variables. Also, bytecode may omit
180 // stores of context parent in certain cases.
181 expected_stores_jit.insert(
182 expected_stores_jit.end(),
183 {":context_var0", "Context.parent", ":context_var0",
184 "Closure.function_type_arguments", "Closure.function",
185 "Closure.context"});
186 expected_stores_aot.insert(
187 expected_stores_aot.end(),
188 {":context_var0", "Closure.function_type_arguments", "Closure.function",
189 "Closure.context"});
190 } else {
191 // These expectations are for AST-based flow graph builder.
192 expected_stores_jit.insert(expected_stores_jit.end(),
193 {"value", "Context.parent", "Context.parent",
194 "value", "Closure.function_type_arguments",
195 "Closure.function", "Closure.context"});
196 expected_stores_aot.insert(expected_stores_aot.end(),
197 {"value", "Closure.function_type_arguments",
198 "Closure.function", "Closure.context"});
199 }
200
201 RunInitializingStoresTest(root_library, "f4", CompilerPass::kJIT,
202 expected_stores_jit);
203 RunInitializingStoresTest(root_library, "f4", CompilerPass::kAOT,
204 expected_stores_aot);
205}
206
207} // namespace dart
208