Flutter Engine
The Flutter Engine
typed_data_aot_test.cc
Go to the documentation of this file.
1// Copyright (c) 2019, 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 <vector>
6
13#include "vm/object.h"
14#include "vm/unit_test.h"
15
16namespace dart {
17
18#if defined(DART_PRECOMPILER)
19
20// This test asserts that we are inlining accesses to typed data interfaces
21// (e.g. Uint8List) if there are no instantiated 3rd party classes.
22ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_Inlining) {
23 const char* kScript =
24 R"(
25 import 'dart:typed_data';
26
27 foo(Uint8List list, int from) {
28 if (from >= list.length) {
29 return list[from];
30 }
31 }
32 )";
33
34 const auto& root_library = Library::Handle(LoadTestScript(kScript));
35 const auto& function = Function::Handle(GetFunction(root_library, "foo"));
36
37 TestPipeline pipeline(function, CompilerPass::kAOT);
38 FlowGraph* flow_graph = pipeline.RunPasses({});
39
40 auto entry = flow_graph->graph_entry()->normal_entry();
41 EXPECT(entry != nullptr);
42
43 LoadFieldInstr* load_field = nullptr;
44 GenericCheckBoundInstr* bounds_check = nullptr;
45 LoadFieldInstr* load_untagged = nullptr;
46 LoadIndexedInstr* load_indexed = nullptr;
47
48 ILMatcher cursor(flow_graph, entry);
49 RELEASE_ASSERT(cursor.TryMatch({
50 kMoveGlob,
51 {kMatchAndMoveLoadField, &load_field},
55 {kMatchAndMoveGenericCheckBound, &bounds_check},
56 {kMatchAndMoveLoadField, &load_untagged},
58 {kMatchAndMoveLoadIndexed, &load_indexed},
60 kMatchDartReturn,
61 }));
62
63 EXPECT(load_field->InputAt(0)->definition()->IsParameter());
64 EXPECT(bounds_check->length()
65 ->definition()
66 ->OriginalDefinitionIgnoreBoxingAndConstraints() == load_field);
67 EXPECT(load_untagged->InputAt(0)->definition()->IsParameter());
68 EXPECT(load_indexed->InputAt(0)->definition() == load_untagged);
69}
70
71// This test asserts that we are inlining get:length, [] and []= for all typed
72// data interfaces. It also ensures that the asserted IR actually works by
73// exercising it.
74ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_FunctionalGetSet) {
75 const char* kTemplate =
76 R"(
77 import 'dart:typed_data';
78
79 void reverse%s(%s list) {
80 final length = list.length;
81 final halfLength = length >> 1;
82 for (int i = 0; i < halfLength; ++i) {
83 final tmp = list[length-i-1];
84 list[length-i-1] = list[i];
85 list[i] = tmp;
86 }
87 }
88 )";
89
90 char script_buffer[1024];
91 char uri_buffer[1024];
92 char function_name[1024];
93 auto& lib = Library::Handle();
94 auto& function = Function::Handle();
95
96 auto check_il = [&](const char* name) {
97 // Fill in the template with the [name].
98 Utils::SNPrint(script_buffer, sizeof(script_buffer), kTemplate, name, name);
99 Utils::SNPrint(uri_buffer, sizeof(uri_buffer), "file:///reverse-%s.dart",
100 name);
101 Utils::SNPrint(function_name, sizeof(function_name), "reverse%s", name);
102
103 // Create a new library, load the function and compile it using our AOT
104 // pipeline.
105 lib = LoadTestScript(script_buffer, nullptr, uri_buffer);
107 TestPipeline pipeline(function, CompilerPass::kAOT);
108 FlowGraph* flow_graph = pipeline.RunPasses({});
109 auto entry = flow_graph->graph_entry()->normal_entry();
110
111 // Ensure the IL matches what we expect.
112 ILMatcher cursor(flow_graph, entry);
113 EXPECT(cursor.TryMatch({
114 // Before loop
115 kMoveGlob,
116 kMatchAndMoveLoadField,
117 kMoveGlob,
118 kMatchAndMoveBranchTrue,
119
120 // Loop
121 kMoveGlob,
122 // Load 1
123 kMatchAndMoveGenericCheckBound,
124 kMoveGlob,
125 kMatchAndMoveLoadField,
126 kMoveParallelMoves,
127 kMatchAndMoveLoadIndexed,
128 kMoveGlob,
129 // Load 2
130 kMatchAndMoveGenericCheckBound,
131 kMoveGlob,
132 kMatchAndMoveLoadField,
133 kMoveParallelMoves,
134 kMatchAndMoveLoadIndexed,
135 kMoveGlob,
136 // Store 1
137 kMatchAndMoveCheckWritable,
138 kMoveParallelMoves,
139 kMatchAndMoveLoadField,
140 kMoveParallelMoves,
141 kMatchAndMoveStoreIndexed,
142 kMoveGlob,
143 // Store 2
144 kMoveParallelMoves,
145 kMatchAndMoveLoadField,
146 kMoveParallelMoves,
147 kMatchAndMoveStoreIndexed,
148 kMoveGlob,
149
150 // Exit the loop.
151 kMatchAndMoveBranchFalse,
152 kMoveGlob,
153 kMatchDartReturn,
154 }));
155 };
156
157 check_il("Uint8List");
158 check_il("Int8List");
159 check_il("Uint8ClampedList");
160 check_il("Int16List");
161 check_il("Uint16List");
162 check_il("Int32List");
163 check_il("Uint32List");
164 check_il("Int64List");
165 check_il("Uint64List");
166 check_il("Float32List");
167 check_il("Float64List");
168 if (FlowGraphCompiler::SupportsUnboxedSimd128()) {
169 check_il("Int32x4List");
170 check_il("Float32x4List");
171 check_il("Float64x2List");
172 }
173}
174
175// This test asserts that we get errors if receiver, index or value are null.
176ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_FunctionalIndexError) {
177 const char* kTemplate =
178 R"(
179 import 'dart:typed_data';
180 void set%s(%s list, int index, %s value) {
181 list[index] = value;
182 }
183 )";
184
185 char script_buffer[1024];
186 char uri_buffer[1024];
187 char function_name[1024];
188 auto& lib = Library::Handle();
189 auto& function = Function::Handle();
190 auto& arguments = Array::Handle();
191 auto& result = Object::Handle();
192
193 const intptr_t kIndex = 1;
194 const intptr_t kLastStage = 3;
195
196 auto run_test = [&](const char* name, const char* type,
197 const TypedDataBase& data, const Object& value,
198 int stage) {
199 // Fill in the template with the [name].
200 Utils::SNPrint(script_buffer, sizeof(script_buffer), kTemplate, name, name,
201 type);
202 Utils::SNPrint(uri_buffer, sizeof(uri_buffer), "file:///set-%s.dart", name);
203 Utils::SNPrint(function_name, sizeof(function_name), "set%s", name);
204
205 // Create a new library, load the function and compile it using our AOT
206 // pipeline.
207 lib = LoadTestScript(script_buffer, nullptr, uri_buffer);
209 TestPipeline pipeline(function, CompilerPass::kAOT);
210 FlowGraph* flow_graph = pipeline.RunPasses({});
211 auto entry = flow_graph->graph_entry()->normal_entry();
212
213 // Ensure the IL matches what we expect.
214 ILMatcher cursor(flow_graph, entry, /*trace=*/true);
215 EXPECT(cursor.TryMatch({
216 // LoadField length
217 kMoveGlob,
218 kMatchAndMoveLoadField,
219
220 // Bounds check
221 kMoveGlob,
222 kMatchAndMoveGenericCheckBound,
223
224 // Store value.
225 kMoveGlob,
226 kMatchAndMoveLoadField,
227 kMoveParallelMoves,
228 kMatchAndMoveOptionalUnbox,
229 kMoveParallelMoves,
230 kMatchAndMoveStoreIndexed,
231
232 // Return
233 kMoveGlob,
234 kMatchDartReturn,
235 }));
236
237 // Compile the graph and attach the code.
238 pipeline.CompileGraphAndAttachFunction();
239
240 arguments = Array::New(3);
241 arguments.SetAt(0, stage == 0 ? Object::null_object() : data);
242 arguments.SetAt(
243 1, stage == 1 ? Object::null_object() : Smi::Handle(Smi::New(kIndex)));
244 arguments.SetAt(2, stage == 2 ? Object::null_object() : value);
246
247 // Ensure we didn't deoptimize to unoptimized code.
248 EXPECT(function.unoptimized_code() == Code::null());
249
250 if (stage == kLastStage) {
251 // The last stage must be successful
252 EXPECT(result.IsNull());
253 } else {
254 // Ensure we get an error.
255 EXPECT(result.IsUnhandledException());
256 result = UnhandledException::Cast(result).exception();
257 }
258 };
259
260 const auto& uint8_list =
261 TypedData::Handle(TypedData::New(kTypedDataUint8ArrayCid, 16));
262 const auto& uint8c_list =
263 TypedData::Handle(TypedData::New(kTypedDataUint8ClampedArrayCid, 16));
264 const auto& int16_list =
265 TypedData::Handle(TypedData::New(kTypedDataInt16ArrayCid, 16));
266 const auto& uint16_list =
267 TypedData::Handle(TypedData::New(kTypedDataUint16ArrayCid, 16));
268 const auto& int32_list =
269 TypedData::Handle(TypedData::New(kTypedDataInt32ArrayCid, 16));
270 const auto& uint32_list =
271 TypedData::Handle(TypedData::New(kTypedDataUint32ArrayCid, 16));
272 const auto& int64_list =
273 TypedData::Handle(TypedData::New(kTypedDataInt64ArrayCid, 16));
274 const auto& uint64_list =
275 TypedData::Handle(TypedData::New(kTypedDataUint64ArrayCid, 16));
276 const auto& float32_list =
277 TypedData::Handle(TypedData::New(kTypedDataFloat32ArrayCid, 16));
278 const auto& float64_list =
279 TypedData::Handle(TypedData::New(kTypedDataFloat64ArrayCid, 16));
280 const auto& int8_list =
281 TypedData::Handle(TypedData::New(kTypedDataInt8ArrayCid, 16));
282 const auto& int_value = Integer::Handle(Integer::New(42));
283 const auto& float_value = Double::Handle(Double::New(4.2));
284 // With null safety nulls cannot be passed as non-nullable arguments, so
285 // skip all error stages and only run the last stage.
286 const intptr_t first_stage = kLastStage;
287 for (intptr_t stage = first_stage; stage <= kLastStage; ++stage) {
288 run_test("Uint8List", "int", int8_list, int_value, stage);
289 run_test("Int8List", "int", uint8_list, int_value, stage);
290 run_test("Uint8ClampedList", "int", uint8c_list, int_value, stage);
291 run_test("Int16List", "int", int16_list, int_value, stage);
292 run_test("Uint16List", "int", uint16_list, int_value, stage);
293 run_test("Int32List", "int", int32_list, int_value, stage);
294 run_test("Uint32List", "int", uint32_list, int_value, stage);
295 run_test("Int64List", "int", int64_list, int_value, stage);
296 run_test("Uint64List", "int", uint64_list, int_value, stage);
297 run_test("Float32List", "double", float32_list, float_value, stage);
298 run_test("Float64List", "double", float64_list, float_value, stage);
299 }
300}
301
302#endif // defined(DART_PRECOMPILER)
303
304} // namespace dart
static void run_test(GrDirectContext *dContext, skiatest::Reporter *reporter, BulkRectTest test)
#define EXPECT(type, expectedAlignment, expectedSize)
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
GLenum type
static Object & Handle()
Definition: object.h:407
uint8_t value
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
Definition: dart_vm.cc:33
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
@ kMatchAndMoveBranchTrue
@ kMoveParallelMoves
FunctionPtr GetFunction(const Library &lib, const char *name)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
const char *const function_name
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
static Dart_Handle InvokeFunction(Dart_Handle builtin_library, const char *name)
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define ISOLATE_UNIT_TEST_CASE(name)
Definition: unit_test.h:64